Decorator pattern and other common code for my windows
-
you should only wrap the most top-level widget with the "decorator-widget". You then can move the window, when you install the decorator-widget as an event-filter on the QMenuBar. Check if the mouse press event is in the empty zone by checking QMenuBar::actionAt(pos) == NULL
-
what do you mean? Change it :)
Also i meant that you implement the event filter in the decoration widget. And install this decoration widget as the event filter on your QMenuBar.
You can keep the code very general to make it work in many cases. Show the code of your eventFilter() please.
-
@
TitlelessWindow::TitlelessWindow(QWidget* widget){//...Removing title code and some decorating stuff... also:
widget->setParent(this);
this->layout()->addWidget(widget);}
void TitlelessWindow::mouseMoveEvent(QMouseEvent *event){
QPoint newPos=event->globalPos();
move(newPos - clickPos);
}void TitlelessWindow::mousePressEvent( QMouseEvent * e){
clickPos=e->pos();
QWidget::mousePressEvent(e);
}bool TitlelessWindow::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::MouseMove) {QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
mouseMoveEvent(mouseEvent);
return true;}if (event->type() == QEvent::MouseButtonPress) {
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);
mousePressEvent(mouseEvent);
return true;} else { // standard event processing return QObject::eventFilter(obj, event); }
}@
This is the code of my wrapper class you told me. Then, I decorate QMainWindow with it, and I install its eventFilter for my QMenuBar. This is currently working, but I dont know if its what you meant.
-
ok looks good.
Some tips:-
In TitlelessWindow::eventFilter() you can check if "obj" is of type QMenuBar and if it is cast it. Then you need to use actionAt() otherwise your menu items won't be clickable.
-
Don't route the events from the event filter through your class' event handler. This works in your case, but since they are propagated they may have side effects if you use top-level windows with a parent widget.
@
bool TitlelessWindow::eventFilter(QObject *obj, QEvent *event)
{
...
switch( event->type() )
{
case QEvent::MouseButtonPress:
{
QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event);QMenuBar* menuBar = qobject_cast<QMenuBar*>(obj); if( menuBar ) { if( menuBar->actionAt(mouseEvent->pos()) == 0 ) //accept movement of window }
}
break;
case QEvent::MouseMove:
...
break;
}
return QWidget::eventFilter(obj,event);
};
@Or you could also check in the constrcutor if the widget is a QMainWindow type and install the eventFilter on the menuBar() there.
Then the whole implementation is in 1 class and all you have to do is wrap your widget with this decoration widget. -
-
Thanks for your help,
Regarding in your second tip, I originally put this code inside the eventFilter instead calling the mouseMoveEvent and mousePressEvent, but then, when I clicked, holded and moved the mouse 1px, it was jumping up like 10px for some reason at the start of the movement. Any idea why this behaviour?
-
most probably a mapping issue.
Easiest would be to always use the event's globalPos() and map it to your decoration widget. Then you should be safe. -
Hi,
I had to consider the geometry differences bewteen contained and container to call the QWidget::move function.
Only one question more: How would you add more event hadling for the QMenuBar? I have a wrapper class for the QMenuBar (MyQMenuBar), but I've just made a installEventFilter pointing to the TitlelessWindow class.
-
[quote author="aidaqt" date="1383750043"]
I had to consider the geometry differences bewteen contained and container to call the QWidget::move function.
[/quote]
Thats why i said it's easier to use the event's QMouseEvent::globalPos() and map it to your top-level widget using QWidget::mapFromGlobal(). ;)[quote author="aidaqt" date="1383750043"]
Only one question more: How would you add more event hadling for the QMenuBar? I have a wrapper class for the QMenuBar (MyQMenuBar), but I've just made a installEventFilter pointing to the TitlelessWindow class.[/quote]
sorry but i don't understand the question. -
If I would need to implement an eventFilter in my QMainWindow or MyQMenuBar, how could I achieve it?
Think for example if I wanted to implement more logic in MouseMoveEvent but only for my QMainWindow.And how could I combine my TitlelessWindow with another similar one "DecoratedWindow"?
-
[quote author="aidaqt" date="1383751265"]If I would need to implement an eventFilter in my QMainWindow or MyQMenuBar, how could I achieve it?
Think for example if I wanted to implement more logic in MouseMoveEvent but only for my QMainWindow.
[/quote]If it's for all QMainWindow's then do it like i did in my code for the QMenuBar (using qobject_cast).
If it's jsut for a special window you could subclass from this class and also reimplement eventFilter() and implement the special case and for the rect call the base class implementation...[quote author="aidaqt" date="1383751265"]
And how could I combine my TitlelessWindow with another similar one "DecoratedWindow"?[/quote]
This is just a matter of Object-orientation. Meaning you could subclass TitlelessWindow... or generate a base-class which only implements the parts which are common for both, etc. -
Hi,
More problems, I hope your patience hasnt ran out yet.. :)
-If I apply this TitlelessWindow to a QMainWindow everything is OK, widgets inside are in a centralWidget containing them.
-If a window has not centralWidget, decoration is applied to the concrete widgets inside ( QGroupBox for example) but the other parts of the window are transparent. Why do I need a centralWidget (or one containing the QGroupBoxes)?
-For a reason, QGroupBoxes doesnt inherits the eventFilter, but QTabWidgets does, so I needed to made MyQGroupBox class...
Thanks again
-
you only need to add the decoration-widget to widgets you know they are top-level widgets if i understood you correct what you want to achieve.
And also your decoration window doesn't necessarily need to inherit QMainWindow. It's enough if it looks like this:
@
TitlelessWindow::TitlelessWindow(QWidget* content, QWidget* parent)
: QFrame(parent)
{
//...Removing title code and some decorating stuff... also:QVBoxLayout* layout = new QVBoxLayout; layout->addWidget(content); this->setLayout(layout);
}
@ -
Hi,
My TitlelessWindow inherits from QWidget. This is my Titleless constructor code:
@TitlelessWindow::TitlelessWindow(QWidget* widget) {
//...Decorating stuff...
widget->setParent(this); //Do I really need this?
this->setGeometry(widget->geometry()); //Pick the size. Not working also
this->setLayout(new QGridLayout ());
this->layout()->addWidget(widget); //Add the QMainWindow or other window to the layout of TitlelessWindow. //Do I really need this?
//Decorate the wrapper class
this->setWindowFlags(Qt::FramelessWindowHint);
this->setAttribute(Qt::WA_TranslucentBackground); //This is being propagated to the child widgets if not centralWidget is present@
-
you can leave out the setParent since this is done when you add the widget to the layout anyway.
I would recommend to do the layout like i did, as long as you do not need the QGridLayout. Beside QGridLayout::addWidget(QWidget*) is not like it is intended to be used (QGridLayout expects row and col parameters).
Do you really need the widget attribute "TranslucentBackground"? You could also use QWidget::setMask() for instance.
If you need it, you can try to unset it on the child widget right after you have set it on the frame widget. Or set the attributes at the very first in the constructor.
Or also try QWidget::setAutoFillbackground(true) on the child widget.