I have been looking at the QComboBox source file for a while now and I can't figure out what I need to change so that the icon is positioned above text in a QComboBox.
|-----------------------|
| ----- |
| |icn| |
| ----- |
| Text label here |
-------------------------
The paint method in QCombobox is very simple and references something called QStyleOptionComboBox, but I don't think I want to be making changes here though as this will impact portability.
Would I be better inventing something new to act and behave like a QComboBox?
I should have added that it I am looking at changing both the ListView and selected item i.e the button portion.
In order to handle the icon's (decoration) position in the combo box's pull down view, you need to override its view options QAbstractItemView::viewOptions(). Let's create a custom view and replace the native combo box view with ours:
class ComboView : public QListView
{
protected:
QStyleOptionViewItem viewOptions() const
{
// Set icon on the top and center of combo box item.
QStyleOptionViewItem option = QListView::viewOptions();
option.decorationAlignment = Qt::AlignHCenter | Qt::AlignCenter;
option.decorationPosition = QStyleOptionViewItem::Top;
option.displayAlignment = Qt::AlignCenter;
return option;
}
};
and for the combo box:
QComboBox cb;
cb.setView(new ComboView); // Sets the custom view.
cb.addItem(QIcon("icon.png"), "Item1");
cb.addItem(QIcon("icon.png"), "Item2");
cb.show();
Related
How to create it in qt?
When you click on button - should be shown popup widget and its width should be = button width.
And if main window (main form) drag to another place on the screen - popup widget should continuously follow the button (must be attached to the bottom border of the button).
before click image:
after click image:
Create widget, don't put it any layout, set it's parent to button's parent (lets call it "host"), set window flags to Qt::Window | Qt::FramelessWindowHint
mPopup = new QWidget(this);
mPopup->setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
Override host's resizeEvent and moveEvent and adjust popup's geometry there using button's geometry.
void Host::adjustPopup() {
if (!mPopup->isVisible()) {
return;
}
QRect rect = mButton->geometry();
QPoint bottomLeft = this->mapToGlobal(rect.bottomLeft());
mPopup->setGeometry(QRect(bottomLeft, QSize(rect.width(),200)));
}
void Host::resizeEvent(QResizeEvent *event)
{
QWidget::resizeEvent(event);
adjustPopup();
}
void Host::moveEvent(QMoveEvent *event)
{
QWidget::moveEvent(event);
adjustPopup();
}
full source: button-popup
I’ve created my own CXTabCtrl that extends CTabCtrl and override the DrawItem Function.
During the phase of rewriting the DrawItem Function, I wasn’t able to differentiate between this two states of CTabCtrl Item:
CTabCtrl item is selected and have focus.
CTabctrl item is selected but doesn’t have focus.
By focus I mean the Focus rectangle is not drawing. Here are two images that will help identify the two states:
Here’s the DrawItem current code, in which I can detect the selected states, but still Unable to detect the focus states.
Here’s a part of the DrawItem current code, in which I can detect the selected states, but still Unable to detect the focus states.
void CXtabCtrl::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
BOOL bFontSuccess = FALSE;
CFont* def_font = NULL;
CFont font_italic;
TC_ITEM tci;
CRect rect(lpDrawItemStruct->rcItem);
wchar_t szTabText[256];
wmemset(szTabText,_T('\0'),256);
RECT rectComplet;
GetClientRect(&rectComplet);
CBrush brtmp(ColorCategoryBackgroundTop);
int nbItem = GetItemCount();
tci.mask = TCIF_TEXT;
tci.pszText = szTabText;
tci.cchTextMax = sizeof(szTabText) -1;
GetItem(lpDrawItemStruct->itemID, &tci);
BOOL bSelect = (lpDrawItemStruct->itemState & ODS_SELECTED) &&
(lpDrawItemStruct->itemAction & (ODA_SELECT | ODA_DRAWENTIRE));
BOOL bfocus = (lpDrawItemStruct->itemState & ODS_FOCUS) &&
(lpDrawItemStruct->itemAction & (ODA_FOCUS | ODA_DRAWENTIRE));
if (bSelect)//Draw In a Specific Way
if (bFocus) //Draw In a Specific Way
}
So, I would be grateful if someone can describe the proper way to detect the two states of a CTabCtrl Item “Selected & Focused”, “Selected & But not focused”
For a standard tab control, the UI will not always draw the focus rectangle. To see the focus rectangle, the tab control must have WS_TABSTOP flag.
The focus rectangle will then be visible when user clicks the Tab key to go through the dialog's controls, or when Alt key is pressed and tab control has focus.
The focus rectangle should be drawn automatically for owner draw tab control when applicable. Make sure WS_TABSTOP is set for tab control (in dialog editor, go to tab control's properties and set "Tabstop = true")
BOOL focused = selected && (GetFocus() == this); will always be TRUE when user clicks on the tab control. ODS_NOFOCUSRECT will indicate if focus rectangle is not requested by the UI. See the example below.
Side note, sizeof(szTabText) returns the wrong value for wchar_t. Use _countof(szTabText) or sizeof(szTabText)/sizeof(*szTabText)
void CXtabCtrl::DrawItem(LPDRAWITEMSTRUCT di)
{
CDC* pDC = CDC::FromHandle(di->hDC);
TC_ITEM tci;
wchar_t text[256] = { 0 };
tci.mask = TCIF_TEXT;
tci.pszText = text;
tci.cchTextMax = _countof(text);
GetItem(di->itemID, &tci);
BOOL selected = di->itemState & ODS_SELECTED;
BOOL focused = selected && (GetFocus() == this);
//The UI may not be drawing focus rectangle, even if we click on the tab
if(di->itemState & ODS_NOFOCUSRECT)
focused = FALSE;
CString str;
if(selected) str += L"SEL ";//indicate selected
if(focused) str += L"FOC ";//indicate focused
CRect rect(di->rcItem);
pDC->TextOut(rect.left, rect.top, str);
}
I am trying to implement a QComboBox, which holds QIcon and QString, like this:
QComboBox.addItem(icon, label);
I want the icons to be visible in the drop-down list, but not in the toolbar. Only the string should be visible after item selection.
Is there an easy way to do this?
To solve this problem, it is enough to override the paintEvent method, taking the default implementation from the sources. Before drawing control QStyle::CE_ComboBoxLabel, it is necessary to set an invalid value of the QStyleOptionComboBox.currentIcon
This works well if the combobox is not editable, otherwise there is an empty space on the left side intended for drawing the icon. Looking at the source, I found out that the combobox changes the geometry of the QLineEdit. If the current element has an icon then geometrically QLineEdit will shift to the right. In order to prevent this in the same paintEvent, it is necessary to force the geometry of QLineEdit without taking into account the icon.
The following code takes this into account and works well in both modes:
class ComboBox : public QComboBox {
public:
ComboBox(QWidget *parent = nullptr)
: QComboBox(parent) { }
protected:
void paintEvent(QPaintEvent *) override {
QStylePainter painter(this);
painter.setPen(palette().color(QPalette::Text));
// draw the combobox frame, focusrect and selected etc.
QStyleOptionComboBox opt;
initStyleOption(&opt);
painter.drawComplexControl(QStyle::CC_ComboBox, opt);
// draw the icon and text
opt.currentIcon = QIcon();
if (auto le = lineEdit()) {
le->setGeometry(style()->subControlRect(QStyle::CC_ComboBox, &opt, QStyle::SC_ComboBoxEditField, this));
} else {
painter.drawControl(QStyle::CE_ComboBoxLabel, opt);
}
}
};
The best way is to set a delegate and to draw the items yourself.
Then you can choose when or not to draw the icon ( decorationRole ), you can choose not to draw the icon for the index which is the current index.
I could find a quick example on how to use a delegate on combobox:
http://programmingexamples.net/wiki/Qt/Delegates/ComboBoxDelegate
But I am afraid it is not the most easist of the ways.
Good luck!
I have QListView with a custom delegate
Custom delegate paint method:
QStyleOptionViewItemV4 opt = option;
initStyleOption(&opt,index);
opt.decorationSize = QSize(deco_size,deco_size);
opt.decorationAlignment |= Qt::AlignCenter;
opt.displayAlignment |= Qt::AlignCenter;
opt.decorationPosition = QStyleOptionViewItem::Top;
opt.font.setBold(true);
const QWidget *widget = opt.widget;
QStyle *style = widget ? widget->style() : QApplication::style();
style->drawControl(QStyle::CE_ItemViewItem,&opt,painter);
My problem is that If I set stylesheet for my QListView::item
e.g.:
#lv::item:selected:active { background: red; }
it won't work!
If I'm using internal, none custom delegate everything is fine.
2.
I only use custom delegate to put the decoration icon to the top of the text, is there a stylesheet option to force the icon to appear on top?
I missed the 4th parameter of QStyle::drawControl function "widget":
style->drawControl(QStyle::CE_ItemViewItem,&opt,painter,widget);
Here, QStyledItemDelegate source code, paint method:
https://qt.gitorious.org/qt/webkit/source/435bbd4be73768f617e4a4083a345d1d8d62daa3:src/gui/itemviews/qstyleditemdelegate.cpp#L444
I am trying to display a square image and have an X in the top right corner (half in the image half outside) to close the image. I dont know of a layout manager that will allow me to do that. How do I implement this?
+---------O <- close button
| |
| |
+---------+
There will be a lot to implement here. I've accomplished this in the following way:
Step 1. Subclass QLabel to make it possible to capture mouse clicks. In the header declare signals Clicked and Pressed, and override the proper mouse events.
LabelButton::LabelButton(QWidget *parent) : QLabel(parent)
{
}
void LabelButton::mouseReleaseEvent(QMouseEvent *event){
emit Clicked();
event->accept();
}
void LabelButton::mousePressEvent(QMouseEvent *event){
emit Pressed();
event->accept();
}
Step 2. Add a LabelButton called xbutton containing an circular 'x' image to your widget at the location that you desire.
Example (This would be in your setupUi function):
...
xbutton = new LabelButton(MainWidget);
xbutton->setObjectName(QString::fromUtf8("xbutton"));
xbutton->setGeometry(QRect(0, 0, 31, 31));
xbutton->setPixmap(QPixmap(QString::fromUtf8(":/xbutton.gif")));
xbutton->setAlignment(Qt::AlignCenter);
...
Step 3. Create your widget. Set its background to transparent, and make sure its size includes room for the 'x' close button. Note: Setting your background to transparent means your widget will have to contain some child widget that accepts input from the user.
Example:
mywidget::mywidget(QWidget *parent): QWidget(parent){
setupUi(this);
moving=false; // notice that you must declare this bool for Step 4.
offset=QPoint(0,0); // Also a QPoint for Step 4
#if defined(Q_WS_MAC) //These values worked for me with the Mac OS 10.5 SDK
this->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint | Qt::Window);
QPalette pal = this->palette();
pal.setColor(this->backgroundRole(), Qt::transparent);
this->setPalette(pal);
#elif defined(Q_WS_WIN)//These values worked for me on Windows XP/Vista/7
this->setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |Qt::FramelessWindowHint | Qt::Window);
setStyleSheet("background:transparent;");
setAttribute(Qt::WA_TranslucentBackground);
#endif
connect(xbutton,SIGNAL(Clicked()),this,SLOT(hide()));
}
Now you have the functionality that you originally desired. When you click the xbutton, the window will close. But you will not have normal move functionality until you implement that.
Step 4. Implement move functionality to your widget.
/*
FUNCTION:mousePressEvent
used to help move the widget since there is no title bar, sets the initial offset of the mouse
*/
void mywidget::mousePressEvent(QMouseEvent *event){
if((event->button() == Qt::LeftButton)) {
moving = true;
offset = event->globalPos() - this->pos();
}
}
/*
FUNCTION:mouseReleaseEvent
used to help move the widget since there is no title bar, releases the "moving" attribute
*/
void mywidget::mouseReleaseEvent(QMouseEvent *event){
if(event->button() == Qt::LeftButton) {
moving = false;
}
}
/*
FUNCTION:mouseMoveEvent
used to help move the widget since there is no title bar
*/
void mywidget::mouseMoveEvent(QMouseEvent *event){
if(moving){
QPoint global = event->globalPos();
this->setGeometry(global.x()-offset.x(),global.y()-offset.y(),this->width(),this->height());
}
}
I found this way to be of most use to me, because I needed lots of functionality out of my customized slick-looking widget.
I really enjoy creative user interfaces and I hope that yours looks really sleek when you get it finished!