Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Mailing lists / lazarus / 2011-07

TTabControl (d)evolution

25 messages · 2 people · –

Reply by email Subscribe to this list Your subscription

Read-only archive. Use j and k to move between messages.

Felipe Monteiro de Carvalho
to Hans-Peter Diettrich: The implementation of the TTabControl seems to cause headaches, and reveals wrong…
Hans-Peter Diettrich wrote
IMO we should accept that a Delphi compatible TTabControl doesn't exist yet, for widgetsets other than Win32. What can/should we do, when it turns out that such a control can not be implemented in all widgetsets?

I bet that it can be implemented in all widgetsets. I don't use TTabControl, so I could be wrong, but I think that you could implement it like this in Gtk2:

gtk_widget
->gtk_notebook
->gtk_widget

3 widgets put together. This should work fine if you can make gtk_notebook show only the tabs without a client area.

Another idea: Override all methods which use WS routines and create a handle for TCustomControl instead and custom draw the entire control.

Show quoted text 5 lines
Why not offer the new implementation (TTabs/TNewTabControl) as TTabControl, to make Delphi migrants happy, and restrict its use to only the supported widgetsets? At the same time the old implementation can be renamed into TLazTabControl, that is not fully Delphi compatible, but immediately usable with all widgetsets?

We cannot have a control in the LCL which works in only 1 platform, if it is really like that, then the current implementation is better.

And since the old TTabControl and TNotebook effectively share the same code, except for the control that shows the tabs, it would be a good idea to merge both into a common base class.

A very bad idea, they are not even located in the same unit... plus their class hierarchy is not even remotely similar. In fact, I don't see anything common between them. TNotebook many pages and no tabs and no border. TTabControl has 1 page, has border and has tabs.

Making unrelated stuff together because they look similar from a first view to save code duplication can bring a heavy penalty in terms of code stability and maintenability. Just think that someone tryes in the future to fix TTabControl and by accident breaks TNotebook... This lesson was learned with the gtk1/gtk2 mess.

In fact, I already predict that even sharing code between TTabControl and TPageControl is a huge risk, changes could break TPageControl. The safest would be starting a new WS object for TTabControl ...

Show quoted text 6 lines
More things can be simplified, in detail the difference between visible and invisible pages, which applies *only* to the TPageControl. part out of the widgetsets into TPageControl, the implementation of the widgetsets and all TCustomTabControl descendants could be simplified a lot. Then also the restrictions would become more obvious, introduced by the various widgetsets.

Why *only* in bold. TCustomTabControl has only 2 descendents, so TPageControl is 50% of it's descendents.

Felipe Monteiro de Carvalho

Reply by email

Hans-Peter Diettrich

The implementation of the TTabControl seems to cause headaches, and reveals wrong assumptions of the devlopers about the behaviour of that control :-(

When I recently submitted an patch, with a new implementation of the TTabControl (named TTabs in the patch), this implementation was tested with the Win32 widgetset, and was fully compatible with the Delphi TTabControl - in contrast to any preceding implementation.

Felipe acted a bit too fast, and replaced the TTabControl by the new implementation, without waiting for the complete implementation with all widgetsets. The consequential problems showed up very soon :-(

When consequently problems with other widgetsets occured, he removed the intented implementation of TTabs (TNewTabControl), making it unusable at all. This step was undone later.

When it turned out that the IDEInspector uses a TTabControl, and stopped working (#19803) due to an misplaced override of ChildClassAllowed, this problem was not fixed in the suggested and obvious way (by moving that method into TPageControl). Instead the wrong assumption was stated, that a TTabControl cannot have child controls - what is not true for a Delphi compatible control.

So where are we now? We are back to the old TTabControl, that is not Delphi compatible, and have a TNewTabControl without an designer. Was this worth all the trouble?

IMO we should accept that a Delphi compatible TTabControl doesn't exist yet, for widgetsets other than Win32. What can/should we do, when it turns out that such a control can not be implemented in all widgetsets?

Why not offer the new implementation (TTabs/TNewTabControl) as TTabControl, to make Delphi migrants happy, and restrict its use to only the supported widgetsets? At the same time the old implementation can be renamed into TLazTabControl, that is not fully Delphi compatible, but immediately usable with all widgetsets?

And since the old TTabControl and TNotebook effectively share the same code, except for the control that shows the tabs, it would be a good idea to merge both into a common base class. The difference between both controls is implemented in their Pages classes (TUNBPages, TTabControlNoteBookStrings), so that effectivly only their constructors have to be different, where this object is created. An even simpler solution were to make the tabs switchable ON/OFF.

More things can be simplified, in detail the difference between visible and invisible pages, which applies *only* to the TPageControl. By moving that part out of the widgetsets into TPageControl, the implementation of the widgetsets and all TCustomTabControl descendants could be simplified a lot. Then also the restrictions would become more obvious, introduced by the various widgetsets.

DoDi

Reply by email

Hans-Peter Diettrich
to Felipe Monteiro de Carvalho: On Sun, Jul 31, 2011 at 6:49 PM, Hans-Peter Diettrich I bet that it can be implemented in…
Show quoted text 14 lines
> IMO we should accept that a Delphi compatible TTabControl doesn't exist yet, > for widgetsets other than Win32. What can/should we do, when it turns out > that such a control can not be implemented in all widgetsets? I bet that it can be implemented in all widgetsets. I don't use TTabControl, so I could be wrong, but I think that you could implement it like this in Gtk2: gtk_widget ->gtk_notebook ->gtk_widget 3 widgets put together. This should work fine if you can make gtk_notebook show only the tabs without a client area.

Fine, when will the implementation be finished?

Show quoted text 8 lines
> Why not offer the new implementation (TTabs/TNewTabControl) as TTabControl, > to make Delphi migrants happy, and restrict its use to only the supported > widgetsets? At the same time the old implementation can be renamed into > TLazTabControl, that is not fully Delphi compatible, but immediately usable > with all widgetsets? We cannot have a control in the LCL which works in only 1 platform, if it is really like that, then the current implementation is better.

After all the compatibility discussions I have the strong impression, that Delphi compatibility should be reconsidered. It's easy to provide fully compatible components for the Win32 platform, so why do we offer a very different implementation of the TTabControl, that can not be Delphi compatible by design???

Show quoted text 9 lines
> More things can be simplified, in detail the difference between visible and > invisible pages, which applies *only* to the TPageControl. > part out of the widgetsets into TPageControl, the implementation of the > widgetsets and all TCustomTabControl descendants could be simplified a lot. > Then also the restrictions would become more obvious, introduced by the > various widgetsets. Why *only* in bold. TCustomTabControl has only 2 descendents, so TPageControl is 50% of it's descendents.

But 50% of the widgetset code is related only to TPageControl, and makes a TTabControl implementation overly complicated. E.g. a TCustomPage has to be used to transfer attributes to the widgetset, and TCustomPage is related only to TPageControl. When all the tab attributes are moved into an record, then it's no more required that all tabbed controls must create dummy page controls for every tab. And as mentioned above, invisible pages must not be handled in the widgetsets, this can be done exclusively in the TPageControl. When we finally implement a widget with a single client area, that area can be filled with nothing, pages or whatsoever, under full control of the controls. Then we can have a paged notebook again, by simply connecting the tab selection event to an untabbed notebook. Currently the situation is reversed, every tabbed control has to use a TPageControl internally, for the only purpose to show the tabs :-(

DoDi

Reply by email

Felipe Monteiro de Carvalho
to Hans-Peter Diettrich
Hans-Peter Diettrich wrote
But 50% of the widgetset code is related only to TPageControl

This simply convinces me more that TTabControl should have a separate widgetset implementation.

Trying to modify the current WS implementation for TPageControl will most likely result in breaking TPageControl.

Felipe Monteiro de Carvalho

Reply by email

Felipe Monteiro de Carvalho
to Hans-Peter Diettrich: Fine, when will the implementation be finished? After all the compatibility discussions I…
Hans-Peter Diettrich wrote 5 lines
After all the compatibility discussions I have the strong impression, that Delphi compatibility should be reconsidered. It's easy to provide fully compatible components for the Win32 platform, so why do we offer a very different implementation of the TTabControl, that can not be Delphi compatible by design???

This was already discussed multiple times, and there are always 3 answers:

1> First of all, it won't happen

2> Just because implementing a control X in platform Y is not easy, it doesn't mean it is impossible. And if that is the only reason to drop Delphi compatibility, then it's not good enough. I'm quite sure much harder controls were already implemented using native widgets then TTabControl...

3> If the native control really cannot do it, we can always custom draw it in one of the following ways:
* only in the problematic platform
* or everywhere (unpopular, but that's how TTabControl currently works)

It the case of TTabControl one idea is custom drawing the entire control, with themes it looks native, and that's how it already works now. Finishing the current TTabControl would probably be easier to do then finishing your new implementation.

Felipe Monteiro de Carvalho

Reply by email

Hans-Peter Diettrich
to Felipe Monteiro de Carvalho
It the case of TTabControl one idea is custom drawing the entire control, with themes it looks native, and that's how it already works now. Finishing the current TTabControl would probably be easier to do then finishing your new implementation.

I couldn't find any code where a TTabControl is painted?

Instead I found a TCustomTabControl created in the TTabControlNoteBookStrings...

DoDi

Reply by email

Felipe Monteiro de Carvalho
to Hans-Peter Diettrich
Hans-Peter Diettrich wrote
I couldn't find any code where a TTabControl is painted?

lcl/include/tabcontrol.inc:

procedure TTabControl.Paint;
var
  ARect: TRect;
  TS: TTextStyle;
  Details: TThemedElementDetails;
begin
  //DebugLn(['TTabControl.Paint Bounds=',dbgs(BoundsRect),'
ClientRect=',dbgs(ClientRect),' CientOrigin=',dbgs(ClientOrigin)]);
  // clear only display area since button area is painted by another control
  // draw a frame
  ARect := ClientRect;
  AdjustDisplayRectWithBorder(ARect);
  Details := ThemeServices.GetElementDetails(ttPane);
  ThemeServices.DrawElement(Canvas.Handle, Details, ARect);
  InflateRect(ARect,BorderWidth,BorderWidth);
  Canvas.Frame3d(ARect, BorderWidth, bvRaised);
  if (csDesigning in ComponentState) and (Caption  '') then
  begin
    ARect:=GetDisplayRect;
    TS := Canvas.TextStyle;
    TS.Alignment:=taCenter;
    TS.Layout:= tlCenter;
    TS.Opaque:= false;
    TS.Clipping:= false;
    Canvas.TextRect(ARect, ARect.Left, ARect.Top, Caption, TS);
  end;
end;
Instead I found a TCustomTabControl created in the TTabControlNoteBookStrings...

Yes, but this control is not rendered to the screen, it is utilized to call methods from this class.

Felipe Monteiro de Carvalho

Reply by email

Felipe Monteiro de Carvalho
to Hans-Peter Diettrich: [...] Only the frame is drawn by the TTabControl, the tabs most probably still are…
Hans-Peter Diettrich wrote
Only the frame is drawn by the TTabControl, the tabs most probably still are painted by the TCustomTabControl.

Indeed, you are correct:

constructor TTabControlNoteBookStrings.Create(TheTabControl: TTabControl);
begin
  inherited Create(TheTabControl);
  FNoteBook := TCustomTabControl{%H-}.Create(nil);
  FNoteBook.ControlStyle := FNoteBook.ControlStyle + [csNoDesignSelectable];
  FNoteBook.Parent := TabControl;

What a mess ...

But I wonder how dropping client controls keeps working if it is done like that. I would think that the TCustomTabControl uses the client space, so it would not be possible to drop client controls.

Felipe Monteiro de Carvalho

Reply by email

Hans-Peter Diettrich
to Felipe Monteiro de Carvalho: On Mon, Aug 1, 2011 at 1:41 PM, Hans-Peter Diettrich lcl/include/tabcontrol.inc:…
Show quoted text 16 lines
On Mon, Aug 1, 2011 at 1:41 PM, Hans-Peter Diettrich wrote: > I couldn't find any code where a TTabControl is painted? lcl/include/tabcontrol.inc: procedure TTabControl.Paint; var ARect: TRect; TS: TTextStyle; Details: TThemedElementDetails; begin //DebugLn(['TTabControl.Paint Bounds=',dbgs(BoundsRect),' ClientRect=',dbgs(ClientRect),' CientOrigin=',dbgs(ClientOrigin)]); // clear only display area since button area is painted by another control // draw a frame

[...]

Only the frame is drawn by the TTabControl, the tabs most probably still are painted by the TCustomTabControl.

Show quoted text 5 lines
> Instead I found a TCustomTabControl created in the > TTabControlNoteBookStrings... Yes, but this control is not rendered to the screen, it is utilized to call methods from this class.

The rename of TCustomNotebook into TCustomTabControl results in an illegal typecast in TTabControl.DrawTab:
FOnDrawTab(TCustomTabControl(Self),TabIndex,Rect,Active) with

   TDrawTabEvent = procedure(Control: TCustomTabControl; TabIndex: Integer;
     const Rect: TRect; Active: Boolean) of object;

Here every user code most probably will fail, because TTabControl is no more based on TCustomTabControl.

DoDi

Reply by email

Felipe Monteiro de Carvalho
to Felipe Monteiro de Carvalho: On Tue, Aug 2, 2011 at 7:11 PM, Hans-Peter Diettrich Indeed, you are correct: constructor…

Ok,

Now I see that it works like this:

TTabControl (TCustomControl)
-> TCustomTabControl
-> other client controls

TCustomTabControl has the lowest Z-order, so it is always bellow other controls.

So, it works fine except for two issues:

1> It has a wrong class hierarchy
2> Tabs.Objects doesn't work

One thing however. The Delphi docs don't say that Tabs.Objects should contain a list of all objects visible for a given tab.

http://docs.embarcadero.com/products/rad_studio/delphiAndcpp2009/HelpUpdate2/EN/html/delphivclwin32/ComCtrls_TTabControl_Tabs.html

Felipe Monteiro de Carvalho

Reply by email

Hans-Peter Diettrich
to Felipe Monteiro de Carvalho: On Tue, Aug 2, 2011 at 7:11 PM, Hans-Peter Diettrich Indeed, you are correct: constructor…
Show quoted text 19 lines
On Tue, Aug 2, 2011 at 7:11 PM, Hans-Peter Diettrich wrote: > Only the frame is drawn by the TTabControl, the tabs most probably still are > painted by the TCustomTabControl. Indeed, you are correct: constructor TTabControlNoteBookStrings.Create(TheTabControl: TTabControl); begin inherited Create(TheTabControl); FNoteBook := TCustomTabControl{%H-}.Create(nil); FNoteBook.ControlStyle := FNoteBook.ControlStyle + [csNoDesignSelectable]; FNoteBook.Parent := TabControl; What a mess ... But I wonder how dropping client controls keeps working if it is done like that. I would think that the TCustomTabControl uses the client space, so it would not be possible to drop client controls.

I just tested a raw TCustomTabControl, and it worked immediately as a TTabControl with the Win32 widgetset. After moving ChildClassAllowed into TPageControl, it allows to add components of any type to its client area. If you can verify this for the other widgetsets[1], the whole implementation of TTabControl reduces to the handling (almost publishing) of the properties.

If this doesn't work, the TTabControl implementation still can be simplified. No properties have to be duplicated in the TTabControl, when above FNoteBook is moved into TTabControl, and all properties are delegated to that FNoteBook. I already tried to implement this in TNewTabControl, but I must have done something wrong, it didn't show any tabs :-(

Another annoying issue is the lack of tab "decoration" (images, buttons, menu) in the Win32 widgetset. E.g. the source editor windows have no page menu and Style with this widgetset - this may be the reason for the Paint routine, but if so, it never was finished. The menu for the tabs is the same as for the source code client, images and close buttons are not shown, despite the option in Tools > Options.

[1] Short test with an empty form:

procedure TForm1.FormCreate(Sender: TObject);
var
   btn: TButton;
begin
//declare Tabs:TCustomTabControl in the form!
   Tabs := TCustomTabControl.Create(self);
   Tabs.Height := 50; //for now
   Tabs.Parent := self;
   Tabs.Align := alTop;
//add some tabs
   Tabs.Pages.Add('tab 1');
   Tabs.Pages.Add('tab 2');
//add something to the client area
   btn := TButton.Create(self);
   btn.Caption := 'test';
   btn.Parent := Tabs; //move ChildClassAllowed into TPageControl!
   btn.Align := alRight;
end;

Depending on the outcome of your test, I can rewrite TNewTabControl with the simplified implementation. I also can try to fix GetHitTestInfo, so that the showing of the popup menu can be delegated to the proper control or client.

More properties are missing, at least from the Win32 widgetset. The problem is that in the current implementation all properties are passed to the widgetsets in TCustomPage parameters, so that this class must be extended with all currently unhandled properties, or dedicated methods have to be added to TWSCustomTabControl. This can be done later, once we know how to make TTabControl work at all, with all widgetsets.

DoDi

Reply by email

Hans-Peter Diettrich
to Felipe Monteiro de Carvalho: Now I see that it works like this: TTabControl (TCustomControl) -> TCustomTabControl ->…
Show quoted text 9 lines
Ok, Now I see that it works like this: TTabControl (TCustomControl) -> TCustomTabControl -> other client controls TCustomTabControl has the lowest Z-order, so it is always bellow other controls.

Ah, then it may be necessary to restrict the background painting and putting other controls into the DisplayRect, instead of the ClientRect of the TTabControl.

BTW the Win32 widgetset already implements an according ClientRect, that excludes the tabs. AutoSize can be implemented easily, by adjusting

  tabctrl.Height := tabctrl.Height - tabctrl.ClientHeight

but this does not work yet with other widgetsets.

Show quoted text 7 lines
So, it works fine except for two issues: 1> It has a wrong class hierarchy 2> Tabs.Objects doesn't work One thing however. The Delphi docs don't say that Tabs.Objects should contain a list of all objects visible for a given tab.

Right, Tabs.Objects[] is unused in the control itself, child controls are stored in Controls[], as usual. Nonetheless Delphi allows to store components in Objects[], so that it were a nice place to hold the (visible) pages, as I could verify with a test program (see TNewTabControl.DoChange). Then a TPageControl would become only a special case of a tabbed control, with the page control types restricted to TTabSheets/TCustomPage. The other use were for notebook docking, where the docked clients can be stored in Objects[], with automatic switching by clicking the tabs.

As for the implementation of the Tabs.Objects[], I based the Tabs[] of TNewTabControl on TStringList, instead of TStrings, so that Tabs.Objects[] is available immediately. Only the notifications for Add and Delete from Tabs[] have to be added to that class. The TCustomTabControl.FAccess is a general TStrings, compatible also with a TStringList (see the various constructors).

DoDi

Reply by email

Felipe Monteiro de Carvalho
to Hans-Peter Diettrich: I just tested a raw TCustomTabControl, and it worked immediately as a TTabControl with…
Hans-Peter Diettrich wrote 5 lines
I just tested a raw TCustomTabControl, and it worked immediately as a TTabControl with the Win32 widgetset. After moving ChildClassAllowed into TPageControl, it allows to add components of any type to its client area. If you can verify this for the other widgetsets[1], the whole implementation of TTabControl reduces to the handling (almost publishing) of the properties.

I am amazed that this worked in Carbon and in Qt, although I still have to test the much less reliable Gtk.

But even if this works, TNewTabControl still lacks a designer, and I am not sure how one could design it. The pages will occupy all the client area, so it won't be possible to drop controls. Maybe one would need to create a new Sheet class for it and set it as invisible in the designer?

This change seams correct in any case so I commited in rev 31849 the change to ChildClassAllowed and I ifdefed TNewTabControl with {$ifdef LCL_NEW_TABCONTROL} and added a registration for it so that you can set this define and test designing.

Show quoted text 6 lines
More properties are missing, at least from the Win32 widgetset. The problem is that in the current implementation all properties are passed to the widgetsets in TCustomPage parameters, so that this class must be extended with all currently unhandled properties, or dedicated methods have to be added to TWSCustomTabControl. This can be done later, once we know how to make TTabControl work at all, with all widgetsets.

It should not be a problem for TTabControl to have sheets if they are invisible in the designer and if you can drop components directly inside the main component. The existence of sheets seams to be indispensable for gtk for example, but they can be hidden enough so that they don't interfere with the expected usage of the control.

Felipe Monteiro de Carvalho

Reply by email

Felipe Monteiro de Carvalho
to Felipe Monteiro de Carvalho
Felipe Monteiro de Carvalho wrote
I am amazed that this worked in Carbon and in Qt, although I still have to test the much less reliable Gtk.

No, it doesn't work in Gtk2.

The button is never shown.

As for the implementation of the Tabs.Objects[], I based the Tabs[] of TNewTabControl on TStringList, instead of TStrings, so that Tabs.Objects[] is available immediately.

I don't see this with good eyes. Yet another class hierarchy difference? It would be better if we do things 100% correct this time.

Felipe Monteiro de Carvalho

Reply by email

Hans-Peter Diettrich
to Felipe Monteiro de Carvalho: On Tue, Aug 2, 2011 at 11:29 PM, Hans-Peter Diettrich I am amazed that this worked in…
Show quoted text 10 lines
On Tue, Aug 2, 2011 at 11:29 PM, Hans-Peter Diettrich wrote: > I just tested a raw TCustomTabControl, and it worked immediately as a > TTabControl with the Win32 widgetset. After moving ChildClassAllowed into > TPageControl, it allows to add components of any type to its client area. If > you can verify this for the other widgetsets[1], the whole implementation of > TTabControl reduces to the handling (almost publishing) of the properties. I am amazed that this worked in Carbon and in Qt, although I still have to test the much less reliable Gtk.

Fine :-)

I was very astonished myself, when I saw it working out-of-the-box. IMO it should be made work with the remaining widgetsets, when there exist problems at all.

Show quoted text 5 lines
But even if this works, TNewTabControl still lacks a designer, and I am not sure how one could design it. The pages will occupy all the client area, so it won't be possible to drop controls. Maybe one would need to create a new Sheet class for it and set it as invisible in the designer?

I see no need for an extra designer. The tabs should be handled either as implicit part of the component, like e.g. the borders of TGroupBox, or as a child component with Align accoding to the TabPosition.

Dropping controls should work by adding csAcceptControls, which is explicitly removed in the constructor (see according comment). This is another part that should be moved out of the base class, into TPageControl.

This change seams correct in any case so I commited in rev 31849 the change to ChildClassAllowed and I ifdefed TNewTabControl with {$ifdef LCL_NEW_TABCONTROL} and added a registration for it so that you can set this define and test designing.

Okay, I'll restart immmmmediately :-)

The lessons from the preceding tries should help to make the move less painful...

Show quoted text 12 lines
> More properties are missing, at least from the Win32 widgetset. The problem > is that in the current implementation all properties are passed to the > widgetsets in TCustomPage parameters, so that this class must be extended > with all currently unhandled properties, or dedicated methods have to be > added to TWSCustomTabControl. This can be done later, once we know how to > make TTabControl work at all, with all widgetsets. It should not be a problem for TTabControl to have sheets if they are invisible in the designer and if you can drop components directly inside the main component. The existence of sheets seams to be indispensable for gtk for example, but they can be hidden enough so that they don't interfere with the expected usage of the control.

It may help to add the required page widgets in the gtk widgetsets only, and if they cannot be made invisible by omitting Show(), they possibly could have an zero extent, or can be moved outside the component bounds.

DoDi

Reply by email

Hans-Peter Diettrich
to Felipe Monteiro de Carvalho: On Wed, Aug 3, 2011 at 7:44 AM, Felipe Monteiro de Carvalho No, it doesn't work in Gtk2.…
Show quoted text 8 lines
On Wed, Aug 3, 2011 at 7:44 AM, Felipe Monteiro de Carvalho wrote: > I am amazed that this worked in Carbon and in Qt, although I still > have to test the much less reliable Gtk. No, it doesn't work in Gtk2. The button is never shown.

See my thoughts about moving the page widgets out of view. Something like the dummy page could be used as a persistent client area. If nothing helps, the gtk widget can be constructed from two independent tabs and client widgets, internally.

Show quoted text 5 lines
> As for the implementation of the Tabs.Objects[], I based the Tabs[] of TNewTabControl on TStringList, > instead of TStrings, so that Tabs.Objects[] is available immediately. I don't see this with good eyes. Yet another class hierarchy difference? It would be better if we do things 100% correct this time.

The class hierarchy is not affected. Actually every descendant uses its own class for FAccess, it can be any TStrings descendant. The same procedure as for any other component with an Items:TStrings property...

We could add a virtual InitLists method to the base class, that creates and assigns an object to FAccess (and FPageList). But the same could be done in every constructor, with immediate AVs when a descendant constructor fails to initialize these vital members.

DoDi

Reply by email

Felipe Monteiro de Carvalho
to Hans-Peter Diettrich: The good news: adding components at design time works immediately, when csAcceptControls…
Hans-Peter Diettrich wrote
In the long term I still suggest to remove *all* code from the widgetsets, that deals with the TCustomPage parameters as *visual* components, and switch the pages of a TPageControl outside the widgetsets, by simply switching their Visibility accordingly.

That's impossible as you have already seen in Gtk2. Visible page sheets are simply mandatory outside Win32.

In my oppinion the easy way out is the following:

1> Override the Handle creation in TTabControl and make it create a TCustomControl handle
2> Put a TPageControl in the client area, like TTabControl currently does

Felipe Monteiro de Carvalho

Reply by email

Hans-Peter Diettrich
to Felipe Monteiro de Carvalho: On Tue, Aug 2, 2011 at 11:29 PM, Hans-Peter Diettrich I am amazed that this worked in…
Show quoted text 10 lines
On Tue, Aug 2, 2011 at 11:29 PM, Hans-Peter Diettrich wrote: > I just tested a raw TCustomTabControl, and it worked immediately as a > TTabControl with the Win32 widgetset. After moving ChildClassAllowed into > TPageControl, it allows to add components of any type to its client area. If > you can verify this for the other widgetsets[1], the whole implementation of > TTabControl reduces to the handling (almost publishing) of the properties. I am amazed that this worked in Carbon and in Qt, although I still have to test the much less reliable Gtk.

The good news: adding components at design time works immediately, when csAcceptControls is included.

However the internal handling of the pages, created for every added tab, seems to move the pages on top of the Z-order, hiding or invalidating most of the child controls. E.g. a StatusBar can be added to a TCustomTabControl, but will not show any content when tabs are added later. An unaligned button disappears completely :-(

In my former attempt I introduced IsUnpaged, so that the control and widgetsets can know whether the pages (in fPageList) should ever be shown. This turned out to be problematic to implement, in detail with the gtk2 widgetset. So I'll try now to create a dummy page for every tab, but exclude all code from TCustomTabControl that tries to make these pages visible when IsUnpaged=True. When this works with the Win32 widgetset, we can test it with the other widgetsets.

In the long term I still suggest to remove *all* code from the widgetsets, that deals with the TCustomPage parameters as *visual* components, and switch the pages of a TPageControl outside the widgetsets, by simply switching their Visibility accordingly.

DoDi

Reply by email

Hans-Peter Diettrich
to Felipe Monteiro de Carvalho: On Thu, Aug 4, 2011 at 6:12 PM, Hans-Peter Diettrich That's impossible as you have…
Show quoted text 9 lines
On Thu, Aug 4, 2011 at 6:12 PM, Hans-Peter Diettrich wrote: > In the long term I still suggest to remove *all* code from the widgetsets, > that deals with the TCustomPage parameters as *visual* components, and > switch the pages of a TPageControl outside the widgetsets, by simply > switching their Visibility accordingly. That's impossible as you have already seen in Gtk2. Visible page sheets are simply mandatory outside Win32.

I still doubt that ;-)

In my oppinion the easy way out is the following: 1> Override the Handle creation in TTabControl and make it create a TCustomControl handle

I don't understand what you mean :-(

2> Put a TPageControl in the client area, like TTabControl currently does

Then every widgetset must allow, somehow, to use the same control for each tab. This should be possible, since there exists no fixed relationship between the widget in the client area of a tabbed widget, and an LCL control.

DoDi

Reply by email

Felipe Monteiro de Carvalho
to Hans-Peter Diettrich

On Thu, Aug 4, 2011 at 7:27 PM, Hans-Peter Diettrich

I still doubt that ;-)

Well, what do you propose for gtk2 then?

I don't understand what you mean :-(

Override NewInstance and hardcode TWSWinControl as the WidgetSetClass:

class function TLCLComponent.NewInstance: TObject;
begin
  Result := inherited NewInstance;
  WSRegisterClass;
  TLCLComponent(Result).FWidgetSetClass := FindWSComponentClass(Self);
  if TLCLComponent(Result).FWidgetSetClass = nil then
  begin
    {$IFDEF VerboseLCL}
    DebugLn(['TLCLComponent.NewInstance WARNING: missing
FWidgetSetClass ',ClassName]);
    {$ENDIF}
    TLCLComponent(Result).FWidgetSetClass := TWSLCLComponent;
  end;
end;

And also override any method from TCustomTabControl which expects a TWSCustomTabControl, otherwise it will crash.

Felipe Monteiro de Carvalho

Reply by email

Hans-Peter Diettrich
to Felipe Monteiro de Carvalho
On Thu, Aug 4, 2011 at 7:27 PM, Hans-Peter Diettrich > I still doubt that ;-) Well, what do you propose for gtk2 then?

Make the dummy page the one and only page for unpaged tab controls, usable as the client area of the TTabControl.

> I don't understand what you mean :-( Override NewInstance and hardcode TWSWinControl as the WidgetSetClass:

[...]

I still don't understand the need and purpose of all that.

When it's about the event handler types:

IMO it was a bad decision to rename the base class into TCustomTabControl, when it effectively is the base class of TPageControl. Revert it to TCustomNotebook, or name it TCustomPageControl, so that a TCustomTabControl can be derived from that base class.

DoDi

Reply by email

Felipe Monteiro de Carvalho
to Hans-Peter Diettrich
Hans-Peter Diettrich wrote
Make the dummy page the one and only page for unpaged tab controls, usable as the client area of the TTabControl.

It is not clear about which dummy page you are talking about, but I don't see this working.

I still don't understand the need and purpose of all that.

I am getting tired of this endless discussion. I was expecting that you would provide patches for this, but if you just want to talk endlessly about the problem, then we are loosing time here. I don't use TTabControl, so I am not that interrested in doing all the work alone. You provided something like 5% of an implementation which works only in win32 and breaks the code of other people using TTabControl. That's not nearly enough, is it?

I already provided a simple working solution for you to finish TNewTabControl:

1> Override the handle and create a TCustomControl handle 2> Put a TPageControl in the bottom of the control, redirect all methods to work on it
3> Make sure it is compatible with the current TTabControl

If you want to work on that implementation, I can help, if not, this discussion is useless.

There are other alternative implementations, for example implementing a new separate widgetset implementation for TTabControl, but it is much, much larger, and from what I already understood patches won't apear for that, so the only viable solution is the one described above.

Felipe Monteiro de Carvalho

Reply by email

Hans-Peter Diettrich
to Felipe Monteiro de Carvalho
Show quoted text 7 lines
On Fri, Aug 5, 2011 at 12:45 PM, Hans-Peter Diettrich wrote: > Make the dummy page the one and only page for unpaged tab controls, usable > as the client area of the TTabControl. It is not clear about which dummy page you are talking about, but I don't see this working.

A dummy page widget is created on creation of the widget, and used until the first tab is added. This widget should be used also for further tabs.

Show quoted text 5 lines
> I still don't understand the need and purpose of all that. I am getting tired of this endless discussion. I was expecting that you would provide patches for this, but if you just want to talk endlessly about the problem, then we are loosing time here.

I don't like endless work, when something gets into the way on other widgetsets. That's why I want you (or somebody else) prepare the widgetsets first, so that the remaining LCL code will work everywhere, once it has been made work.

Currently I can't debug on my Linux, that's why I cannot dig deeper into the gtk2 problems.

I already provided a simple working solution for you to finish TNewTabControl: 1> Override the handle and create a TCustomControl handle

I still don't understand the purpose of that procedure.

2> Put a TPageControl in the bottom of the control, redirect all methods to work on it

That's the old implementation, no need to reinvent the wheel.

3> Make sure it is compatible with the current TTabControl

I thought that we should make the old (current) implementation more Delphi compatible?

DoDi

Reply by email

Felipe Monteiro de Carvalho
to Hans-Peter Diettrich
Hans-Peter Diettrich wrote
I don't like endless work, when something gets into the way on other widgetsets. That's why I want you (or somebody else) prepare the widgetsets first, so that the remaining LCL code will work everywhere, once it has been made work.

My solution does not require any work in the widgetsets.

> 1> Override the handle and create a TCustomControl handle I still don't understand the purpose of that procedure.

To make it uses WSCustomControl as it's widget, so the control can be implemented like this:

TTabControl (WSCustomControl handle)
-> TPageControl over it's child area
-> child controls are put here, inside the TTabControl, but over the TPageControl

> 2> Put a TPageControl in the bottom of the control, redirect all > methods to work on it That's the old implementation, no need to reinvent the wheel.

The idea is to work only on TNewTabControl and only make the change when it is finished.

The current implementation descends from TCustomControl, it needs to be changed to descend from TCustomTabControl. Of course you can copy it on top of the current TNewTabControl. But that's not as trivial as it sounds, such a control will have a lot of inconsistensy because of duplicate methods all over the place. This needs to be fixed.

> 3> Make sure it is compatible with the current TTabControl I thought that we should make the old (current) implementation more Delphi compatible?

You can copy the old implementation on top of TNewTabControl, then change the inheritance and then fix all the errors. That's one solution. The other is to start clean in TNewTabControl and gradually add code from TTabControl.

Felipe Monteiro de Carvalho

Reply by email

Hans-Peter Diettrich
to Felipe Monteiro de Carvalho
My solution does not require any work in the widgetsets.

I see, you want a control with an embedded notebook.

Show quoted text 10 lines
>> 1> Override the handle and create a TCustomControl handle > I still don't understand the purpose of that procedure. To make it uses WSCustomControl as it's widget, so the control can be implemented like this: TTabControl (WSCustomControl handle) -> TPageControl over it's child area -> child controls are put here, inside the TTabControl, but over the TPageControl

Ah!

DoDi

Reply by email