My widget is frameless because of the sleek looks and so I need to reimplement the resize behaviour.
If I drag the bottom right corner it already works with this code:
void mouseMoveEvent(QMouseEvent *event) {
resize(event->pos().x(), event->pos().y());
}
But what about all the other corners? For example the bottom left corner.
As one expects, it should behave as if the top right corner of the widget would be fixed.
But in the resize function, the upper left corner is fixed, because there is the event.pos()==0.
My idea was to resize the window and then move it, so that it looks as if it would not move, but only change size around the upper left corner. As this leads to flickering and even not the perfect result, is there a better way?
EDIT: Solution:
a) You can define in mousePressEvent:
offset = event->pos();
initialFixedCornerPosX = this->width()+this->pos().x();
initialFixedCornerPosY = this->pos().y();
and in mouseMoveEvent
int x = event->globalX();
int y = event->globalY();
int x_w = offset.x();
int y_w = offset.y();
setGeometry(x-x_w,initialFixedCornerPosY,initialFixedCornerPosX-x+x_w,y-initialFixedCornerPosY);
or
b) in mouseMoveEvent
QRect rect = geometry();
rect.setBottomLeft(event->globalPos());
setGeometry(rect);
To both resize and move the window in one step without flickering, you should use QWidget::setGeometry(QRect) by providing a modified rectangle previously fetched using the corresponding getter function QWidget::geometry().
QRect rect = geometry();
// (then modify...)
setGeometry(rect);
To modify a corner of a QRect, you could either modify each edge or the corners directly. Depending on the rest of your logic, one makes more sense than the other. I'd prefer the second of the following two options:
Example using corners:
If you detect that the user drags the bottom left corner, use
rect.setBottomLeft(event->pos());
However, you of course need to consider edges too, and if you consider corners as separate cases this results in eight cases to be considered in the mouse event.
Example using only edges:
If you detect that the mouse is on the left edge (it might be as well on the top or bottom corner, which are only special cases, so for now we ignore that):
rect.setLeft(event->pos().x());
and if you detect it is on the bottom edge, then
rect.setBottom(event->pos().y());
so if both cases are true, this effectively moves the corner of the rect. So you only need to consider four cases to drag all edges and corners of your window! This assumes that you have a single widget which handles the resize (i.e. your top level widget, which has a margin on the layout to have the children not touch the window edge), and do not add a widget for each corner / edge.
Related
Let's say you have a resizable window with child scrollbar controls, and the scrollbars come and go depending on whether the window contents are large enough to require scrolling.
When both scrollbars are present, a small rectangle is effectively created in the bottom right corner of the window, at their intersection. Is there a clean strategy for clipping that rectangle when drawing on the window, so that you don't paint on it?
I guess my current approach is to obtain the rectangles for each scrollbar, and if those rectangles are not null, then use the rectangles' locations to determine the rectangle that we want to clip. And then call ExcludeClipRect for that rectangle. I guess a similar approach could be used, except with GetSystemMetrics(SM_CXVSCROLL) and GetSystemMetrics(SM_CYVSCROLL) to get the rectangle dimensions.
But is there a more accepted way of doing this, perhaps using some helpful clipping API functions? Thank you for any input.
I have a widget that is used to cover other widgets. Its goal is to block mouse events on all widgets of window but a counted ones (the uncovered widgets). The covering widget paints a semi-translucent image everywhere but over the uncovered widgets.
I succeeded in doing it by using QWidget::setMask and a composition of QRegions made from widget's rects (like ALL.rect - uncovered_widget_1.rect - unconvered_widget_2.rect - ...).
// mask_path is a member of type QPainterPath
// cover_widget is a member of type QWidget*
if (cover_widget->mask().isEmpty()) { // first time
cover_widget->setMask(cover_widget->parentWidget()->rect());
mask_path = {};
}
QPainterPath path;
path.addRect(uncovered_widget_rect);
mask_path.addPath(path);
cover_widget->setMask(cover_widget->mask().subtracted(path.toFillPolygon().toPolygon()));
Now, the specifications have changed a bit and now the holes shall have rounded corners. At a first attempt I changed the addRect with addRoundedRect but masks are binary and the corners do not always render rounded without anti-aliasing:
I can get the visual result I want by disabling the mask and using a clipping path in the paint event handler, but then the mouse events are not going through the widget.
QPainterPath path;
path.addRect(geometry());
painter.setClipPath(path.subtracted(mask_path));
I've unsuccessfully tried to ignore mouse events in the covering widget. AFAIK once an event is ignored it goes up to the parent but doesn't goes down back again to a different child (correct me if I'm wrong please).
I've seen the RenderFlag::IgnoreMask but the only place to use it is through a custom call to QWidget::render, not during standard paint flow.
Is there any way I can get such effect: smooth clipping polygons + letting mouse events to go through only in certain areas?
I've a custom QGraphicsItem which draws nothing but is parent to other QGraphicsItems (like QGraphicsRectItem and so on). My top-level item has (0,0) somewhere "inside" the children Items. This is very inconvenient. I would like to shift the origin to the upper left corner of childrenBoundingRect().
In the picture I've (0,0) of my top-level item somewhere inside of my children items (solid arrow). I would like to shift the origin to the dashed lines. How can I do that?
As result I expect that positioning of the top-level item will be more convenient.
Examples for overriding QGraphicsItem often show the boundingRect() function originating from (0,0). Changing this will change the origin. So, for example, to change it to the centre, where width and height are variables stored internally in the class, you can do this: -
QRectF boundingRect() const
{
return (-width / 2, -height / 2, width, height);
}
I am implementing a function (Using Qt) that is supposed to move a widget to the cursor position as part of a drag and drop functionality.
I have three events that get triggered, mouse down, mouse move, and mouse up. When the mouse moves and is down, a signal is sent to the widget to move itself to the cursor; however, I have encountered some strange behavior.
This simple code:
void Block::moveToCursor()
{
block->move(block->mapFromGlobal(QCursor::pos()));
qDebug() << block->mapFromGlobal(QCursor::pos());
}
where "block" is a QLabel that is a member of Block and is a child of the window's central widget.
It produces this result:
As it can be seen in the debug output, the coordinates are flip flopping (or flickering) every time there is a pixel move. The first coordinated are correct but the second set of coordinates seem to be relative to the top right corner of the window.
I have tried all the mapping functions:
block->move(block->mapFromParent(QCursor::pos()));
-Produces a similar result with the second set of coordinates relative to the center of the window.
block->move(block->mapFrom(this->block, QCursor::pos()));
-This produces an even stranger result. The block does not flicker and moves correctly with respect to the mouse, but the initial position of the block seems to be off by the distance from the top right corner of the computer screen. It also only shows one point when printed out in debug, yet it is moving on the screen. Every time you see Continue Drag, the mouse has moved at least one pixel.
Can someone explain this strange behavior to me and show me the correct way of moving the widget to the cursor at the exact position from where it was originally clicked?
To get the coordinate in relation to the parent (like wanted from move()-Method), you need to use the mapFromGlobal on the parent like this:
block->move(block->parentWidget()->mapFromGlobal(QCursor::pos()));
Just give it a try. The reason for this is, lets consider what happens when you are at position 1,1 inside the child. But the child is for example at position 123,123 from its parent top-left. You would now move it to 1,1, causing it to jump 122 pixels each.
I have two QGraphicsView that are of equal width, one ontop the other in a Vertical Layout.
When I re-size my application window, the QGraphicsView on the bottom does what I expect, it remains at the exact position it started at, however the top view begins to move the scene to the right exposing coordinates that are below x=0(essentially blank padding on the left edge of the View), which I do not want, I need both to behave the same because they correspond to each other.
I must have missed something, because should these views behave exactly the same? I need them to align, as the top view has hidden scroll bars and scrolls horizontally by however much the bottom view is scrolled.
Make sure your resizeAnchor is set to NoAnchor and alignment is Qt::AlignLeft | Qt::AlignTop. You may need to try some other combination to work with your situation.