PDA

View Full Version : QTabBar with optional button to the right of every tab



momesana
6th October 2007, 21:31
Hi, I want to have an extra button on a QTabBar, like the closeButton in Firefox. My current solution is subclassing from QTabWidget and doing the drawing myself. It works but I'm losing the boons of CSS-Styling :(. This way I need to write a new class every time I want to implement a different look. Is there a way to achieve the same effect without losing the Styleability of the widget or is it possible to access the defined stylesheet values without doing all the parsing logic so I can try to take them into consideration when painting the tabbar? ... like getter functions for the properties and also the properties for the subcontrols?

Thanx in advance

jpn
7th October 2007, 12:14
I'm not 100% sure but I think some of those Qt 4 dedicated IDEs out there have such tabs. Of course, it has also been suggested to Trolltech: http://trolltech.com/developer/task-tracker/index_html?method=entry&id=137891

wysota
7th October 2007, 13:09
To be honest QTabBar/QTabWidget API sucks when it comes to modifying it. If you take a look at the sources, it depends heavily on P-IMPL and QStylePainter. In my opinion these classes need to be redesigned. It might be hard because of a need to maintain backward compatibility, but currently QTabBar is surely one of the hardest widgets to extend, so something should be done about it. The least they could do is to introduce the "icon" property (or taking the icon from the widget associated with the tab) and "iconClicked()" signal. That should be doable without introducing any virtual methods.

momesana
7th October 2007, 14:25
Thanks for replying. I guess then I have to use my own class and sacrifice Stylability until the trolls come up with a better solution (I hope they do, even though it doesn't seem like they care too much from looking at the bug entry).

Thanx.

wysota
7th October 2007, 17:09
It could be because it would be hard to implement on MacOS for instance... MacOS tab bars don't have space for icons and it'd look silly if you just sticked an icon there.

momesana
7th October 2007, 17:22
It could be because it would be hard to implement on MacOS for instance... MacOS tab bars don't have space for icons and it'd look silly if you just sticked an icon there.
I've never worked with MacOS so I can't tell but generally having such an optional icon is a good thing, at least when it really is optional. It really improves usability when there are many open tabs like in a browser.

wysota
7th October 2007, 17:38
I know it's a good thing. I've been trying to implement that d**n thing on QTabBar for days... Each time I ended up concluding that currently one needs to implement almost everything from scratch.

jpn
7th October 2007, 19:45
Each time I ended up concluding that currently one needs to implement almost everything from scratch.
Same result here...

wysota
7th October 2007, 19:50
Same result here...

Maybe we should cooperate? It would only be half a scratch each :D

momesana
7th October 2007, 23:16
A joint venture between libQxt and wwidgets :-D and I'll be the consumer of the result :-D.

wysota
7th October 2007, 23:25
Honestly I don't think we'd be able to do anything really useful and platform independent without Trolltech changing the API. The widget simply relies too much on p-impl and style code. Without decoupling those there is not much we can do - there is no API to retrieve the icon rect of each tab for instance.

momesana
8th October 2007, 00:22
The widget simply relies too much on p-impl and style code. Without decoupling those there is not much we can do - there is no API to retrieve the icon rect of each tab for instance.

Well, the Icon (the closebutton) is to always be placed on the right. That's where almost all the implementations of tabbars place the icon. And the icon is always horizontally centered. I have never come across anything else in real software. So the only thing to add is to add some space between the right tab border and the icon. The spacing should somehow be deduced from the Style or the StyleSheets.

In my implementation I've introduced a setSpacing member function that defines how much spacing there is between the three elements (left default icon, text and the right icon) and the surrounding tab borders. So I have three functions that each take a QRectF (the rect that the programmer wants to paint to calculated from what it has really been allocated by tabRect()), and return the area the left icon, the text and the right icon are to be located based on iconSize(), spacing and fontMetrics. It also works when I catch a mousePressEvent and want to find out if the area clicked coincides with the location of the close-icon.

Of course this is not the optimal solution but I guess it is the only quick and dirty way to do it and a trolltech sponsored QTabWidget is (unfortunately) not on the horizon. So why not implement it this way to give the desperate a choice even though it is not optimal? Isn't it possible to get something that works and also respects the Style and additional StyleSheets?

wysota
8th October 2007, 01:01
Well, the Icon (the closebutton) is to always be placed on the right. That's where almost all the implementations of tabbars place the icon. And the icon is always horizontally centered. I have never come across anything else in real software.
The problem is QTabBar already has support for icons and they are placed on the left of the tab. Furthermore a tab can have an icon, but it doesn't have to. A whole tab, a part of it or none of it can be shown in the bar, etc. There are many things to think about and no support for that in public API. And using different styles and stylesheets complicates the situation even more. And what about making space for the icon if the tab text is so wide it occupies the whole width of the tab? Again, no support for that in the API. You'd have to reimplement half of the paint event to make the tab not draw its text and then draw it manually in a correct rectangle.


In my implementation I've introduced a setSpacing member function that defines how much spacing there is between the three elements (left default icon, text and the right icon) and the surrounding tab borders. So I have three functions that each take a QRectF (the rect that the programmer wants to paint to calculated from what it has really been allocated by tabRect()), and return the area the left icon, the text and the right icon are to be located based on iconSize(), spacing and fontMetrics. It also works when I catch a mousePressEvent and want to find out if the area clicked coincides with the location of the close-icon.
I think you simplified it too much. Have you tried it with different styles, tab positions and shapes, icon sizes? I think your solution is fine for some cases, but for others it will fail badly (for instance on Mac where tabs are centered and their contents centered within tabs). Here, look:
http://doc.trolltech.com/4.3/images/macintosh-tabwidget.png

Or the one below (click for a large version), see how complex it is (for instance the partially hidden tab on the right):
1609


Isn't it possible to get something that works and also respects the Style and additional StyleSheets?

In my opinion not without reimplementing half of the class. Believe me, I spent hours thinking about it and came to no conclusions of doing it the right way.

momesana
8th October 2007, 01:32
The problem is QTabBar already has support for icons and they are placed on the left of the tab. Furthermore a tab can have an icon, but it doesn't have to. A whole tab, a part of it or none of it can be shown in the bar, etc.
I paint the widget in the rect tabRect() returns to me. Then the TabBar takes responsibility of displaying the Tabs, sometimes truncating the stuff.



There are many things to think about and no support for that in public API. And using different styles and stylesheets complicates the situation even more.
Yes, Styles and stylesheets are the obstacles that I cannot overcome :(



And what about making space for the icon if the tab text is so wide it occupies the whole width of the tab?
I do it like that. First I make sure that sizeHint returns a value larger then the number of icons + the size of ... so that the minimal size can host the icons that are enabled and the ellipses. Then I pass the text to be displayed to fontMetrics::elipsedText() with the rect that the selfwritten textRect member function has calculated for the text.



Again, no support for that in the API. You'd have to reimplement half of the paint event to make the tab not draw its text and then draw it manually in a correct rectangle.

I think you simplified it too much.
definitely :-). Otherwise I wouldn't have been able to implement it in the first place. Its a brute force implementation that is not reusable at all. I am not even sure it works on windows (gotta try that tomorrow).



Have you tried it with different styles, tab positions and shapes, icon sizes? I think your solution is fine for some cases, but for others it will fail badly (for instance on Mac where tabs are centered and their contents centered within tabs).
I have tried it with different styles but not with different tab positions or shapes. since I've implemented paintEvent() myself and do all the drawing it always looks the same which is the problem in the first place. :-(
Still, a tabWidget with an extra button would really be a nice thing to have, even if the implementaion is limited or not very cleanly implemented. My implementation does not respect styles or stylesheets so it is not reusable. But if there was something which would at least halfway respect such Styles and Stylesheets that would be enough. If trolltech doesn't do it, the next authorities that are obliged (or capable) to implement it are the libQxt team (with jpn as their ambassador to this forum) and the wwwidgets team (which is you wysota). :-D.