HowTo stick QDialog to Screen Borders like Skype do? - qt

A long time ago I tried to find method how to stick QDialog window to screen borders for my small projects like Skype windows do it, but I failed. May be I was looking this code not in the right place, so now I'm looking the solution here, on stack! :)
So, does any one have a deal with some kind of such code, links, samples?
In my opinion, we have to reimplement QDialog moveEvent function, like below, but that code does not working:
void CDialog::moveEvent(QMoveEvent * event) {
QRect wndRect;
int leftTaskbar = 0, rightTaskbar = 0, topTaskbar = 0, bottomTaskbar = 0;
// int top = 0, left = 0, right = 0, bottom = 0;
wndRect = this->frameGeometry();
// Screen resolution
int screenWidth = QApplication::desktop()->width();
int screenHeight = QApplication::desktop()->height();
int wndWidth = wndRect.right() - wndRect.left();
int wndHeight = wndRect.bottom() - wndRect.top();
int posX = event->pos().x();
int posY = event->pos().y();
// Snap to screen border
// Left border
if (posX >= -m_nXOffset + leftTaskbar &&
posX <= leftTaskbar + m_nXOffset) {
//left = leftTaskbar;
this->move(leftTaskbar, posY);
return;
}
// Top border
if (posY >= -m_nYOffset &&
posY <= topTaskbar + m_nYOffset) {
//top = topTaskbar;
this->move(posX, topTaskbar);
return;
}
// Right border
if (posX + wndWidth <= screenWidth - rightTaskbar + m_nXOffset &&
posX + wndWidth >= screenWidth - rightTaskbar - m_nXOffset) {
//right = screenWidth - rightTaskbar - wndWidth;
this->move(screenWidth - rightTaskbar - wndWidth, posY);
return;
}
// Bottom border
if (posY + wndHeight <= screenHeight - bottomTaskbar + m_nYOffset &&
posY + wndHeight >= screenHeight - bottomTaskbar - m_nYOffset) {
//bottom = screenHeight - bottomTaskbar - wndHeight;
this->move(posX, screenHeight - bottomTaskbar - wndHeight);
return;
}
QDialog::moveEvent(event);
}
Thanks.

As you thought you can achieve this in the moveEvent function.
I guess the following code do the trick but since I have nothing to test here I will write some pseudo code:
First get the available screen area:
const QRect screen = QApplication::availableGeometry(this);
// This get the screen rect where you can drag a dialog
Then get the position of your dialog relative to the desktop (if your dialog is a child of an other widget, you need to transform coordinates from widget relative to desktop relative):
const QRect dialog = geometry();
// Do here transformation
Now test if dialog is near screen border
if( abs(dialog.left()-screen.left() < OFFSET )
move(screen.left(), dialog.top();
else if( abs(dialog.top()-screen.top() < OFFSET )
move(dialog.left(), screen.top() )
// etc. for the 2 other cases
Let me know if it works

In the pos property description from the QWidget documentation, there is the following warning about moving a window inside the move event handling method.
Warning: Calling move() or setGeometry() inside moveEvent() can
lead to infinite recursion.
That said, there is no proper way to stick the dialog window inside the screen border.
Note :
The behavior you observed in KDE comes from the Window Manager. Actually, the Window Manager is the one that arranges the application windows (like dialog boxes) to show them on the screen. The KDE Window Manager has an option to make all application windows (called client) stick to the border.

Related

Using CSS setProperty for top does not work well when implement GWT drag

I am implementing a GWT application, have a scroll panel, flow panel which contains image, mouse down/move/up to drag the flow panel in scroll panel.
Left part (x direction) works perfectly, however, the same code for top (y direction) does not work well, it seems it shake and move unstable.
Somehow the top value is much larger than left which cause the problem, but no idea how it happens and how to make the Y direction work smoothly.
public void mouseDown(MouseDownEvent event)
{
isMouseDown = true;
event.preventDefault();
xoffset = event.getX();
yoffset = event.getY();
Event.setCapture(panel.getElement());
}
public void mouseMove(MouseMoveEvent event) {
int = event.getX();
int y = event.getY();
float left = panel.getAbsoluteLeft();
float top = panel.getAbsoluteTop();
float offset_XX = x - xoffset;
float offset_YY = y - yoffset;
panel.getElement().getStyle().setProperty("position", "absolute");
float newLeft = left + offset_XX;
if (isMouseDown) {
if (newLeft < scrollPanel.getAbsoluteLeft() ) {
offset_XX = offset_XX - Math.abs(scrollPanel.getAbsoluteLeft() -panel.getAbsoluteLeft());
if (Math.abs(offset_XX) > Math.abs(scrollPanel.getOffsetWidth() - panel.getOffsetWidth())) {
if (offset_XX > 0 )
offset_XX = Math.abs(scrollPanel.getOffsetWidth() - panel.getOffsetWidth());
else
offset_XX = -Math.abs(scrollPanel.getOffsetWidth() - panel.getOffsetWidth());
}
panel.getElement().getStyle().setPropertyPx("left", (int)offset_XX);
}
float newtop = top + offset_YY;
if (newtop < scrollPanel.getAbsoluteTop()) {
offset_YY = offset_YY - Math.abs(scrollPanel.getAbsoluteTop() -panel.getAbsoluteTop());
if (Math.abs(offset_YY) > Math.abs(scrollPanel.getOffsetHeight() - panel.getOffsetHeight())) {
if (offset_YY > 0 )
offset_YY = Math.abs(scrollPanel.getOffsetHeight() - panel.getOffsetHeight());
else
offset_YY = -Math.abs(scrollPanel.getOffsetHeight() - panel.getOffsetHeight());
}
panel.getElement().getStyle().setPropertyPx("top", (int)offset_YY);
}
}
}
Long story short, there're too many computations and DOM calls going on in your drag code. I suggest you to incorporate GWT team's solution for draggable/resizable panels following the implementation of com.google.gwt.logging.client.LoggingPopup. It's very fast, elegant, easy to understand and use. You can get the code here https://github.com/stephenh/google-web-toolkit/blob/master/user/src/com/google/gwt/logging/client/LoggingPopup.java

Problems keeping orthographic projection proportional using Qt/OpenGL

I am having a difficult time understanding what values I should use in the call I make to QMatrix4x4::ortho(left, right, bottom, top, near, far).
Specifically, I don't understand the documentation for the values for left,right,bottom, and top. I have a working demo that can draw the OpenGL monkey with a projection matrix. I set up the demo so that when I hit 'O' on the keyboard, it switches the projection matrix from perspective to orthographic. The perspective projection works well in that it keeps the model's aspect ratio constant (i.e. it doesn't stretch in width or height). Here's the function that gets called when either the 'O' or 'P' key gets called and updates m_proj. It's a bit of a hot mess as you can see I've tried a bunch of ideas and none of them really works the way I would like.
Thanks for any insight that would help me understand this. Other helpful details: my view eye is at z=2 facing center (0,0,0) with up being (0,1,0).
void AppGLScene::setProjectionMatrix(void)
{
m_projectionMatrix.setToIdentity();
float windowWidth = rect().width();
float windowHeight = rect().height();
float left, right, bottom, top;
float aratio = (float) windowWidth / (float) windowHeight;
qDebug() << "win wid, win hei" << windowWidth << windowHeight;
// I modify the vertical FOV in an attempt to keep the size of the
// model the same as the vertical size of the window changes.
//
float vFov = 90 * ((float)windowHeight / m_initialWinHeight);
qDebug() << "vFov" << vFov;
switch (m_proj)
{
case PROJ_PERSP:
m_projectionMatrix.perspective(vFov, qreal(windowWidth)/qreal(windowHeight), 0.5, 40);
break;
case PROJ_ORTHO:
default:
// left = rect().x();
// right = rect().x() + rect().width();
// bottom = rect().y();
// top = rect().y() + rect().height();
if (windowWidth > windowHeight)
{
left = -(3.0 - ((float)windowHeight/(float)windowWidth));
right = -left;
bottom = -3.0;
top = 3.0;
}
else
{
left = -3.0;
right = 3.0;
bottom = -(3.0 - ((float)windowWidth/(float)windowHeight));
top = -bottom;
}
qDebug() << "l r b t = " << left << right << bottom << top;
m_projectionMatrix.ortho(left, right, bottom, top, 0.5, 40);
// m_projectionMatrix.ortho(-3.0, 3.0, -3.0, 3.0, 0.5, 40);
// m_projectionMatrix.ortho(-aratio, aratio, -aratio, aratio, 0.5, 40);
break;
}
}
To avoid stretching your objects in either direction, you need to have (right - left) / (top - bottom) match the aspect ratio of the window. Which in your case you can ensure by having right be the value of top multiplied by the aspect ratio.
It looks like you want to use the range [-3.0, 3.0] for the shorter window dimension, and adjust the longer one accordingly. The above then translates into:
if (windowWidth > windowHeight)
{
top = 3.0f;
bottom = -top;
right = top * aratio;
left = -right;
}
else
{
right = 3.0f;
left = -right;
top = right / aratio;
bottom = -top;
}
Note that right / top = aratio for both cases.

QSplitter in two directions

I want to make an app that includes four widgets that are resizable using QSplitter. In this app I would like that all four widgets are resized when I resize the splitter. I realised this by having a horizontal splitter contain two vertical splitters. This way however the vertical splitting only concerns two widgets and not all four. Is there a way to to this "matrix" splitting?
Another possibility to the other answer would be a manual layout with a fancy single resizing handle in the crossing of the four widgets.
Should be done with a couple lines of code using mouse events and setGeometry calls.
Like this (working example) :
(simply add a paint event to draw a handle in the center as you like)
Damn it .. obviously the was a copy'n'paste error with the button labels ; ) I corrected fixed the code ...
FourWaySplitter::FourWaySplitter(QWidget *parent) :
QWidget(parent),
ui(new Ui::FourWaySplitter), m_margin(5)
{
ui->setupUi(this);
m_ul = new QPushButton("Upper Left", this);
m_ur = new QPushButton("Upper Right", this);
m_ll = new QPushButton("Lower Left", this);
m_lr = new QPushButton("Lower Right", this);
setFixedWidth(500);
setFixedHeight(400);
// of course, the following needs to be updated in a sensible manner
// when 'this' is not of fixed size in the 'resizeEvent(QResizeEvent*)' handler
m_handleCenter = rect().center();
m_ul->setGeometry(QRect(QPoint(m_margin,m_margin), m_handleCenter - QPoint(m_margin, m_margin)));
m_ur->setGeometry(QRect(QPoint(width()/2 + m_margin, m_margin), QPoint(width() - m_margin, height()/2 - m_margin)));
m_ll->setGeometry(QRect(QPoint(m_margin, height()/2 + m_margin), QPoint(width()/2 - m_margin, height() - m_margin)));
m_lr->setGeometry(QRect(QPoint(width()/2 + m_margin, height()/2 + m_margin), QPoint(width() - m_margin, height() - m_margin)));
}
void FourWaySplitter::mouseMoveEvent(QMouseEvent * e)
{
if(m_mouseMove) {
QRect newGeo = m_ul->geometry();
newGeo.setBottomRight(e->pos() + QPoint(-m_margin, -m_margin));
m_ul->setGeometry(newGeo);
newGeo = m_ur->geometry();
newGeo.setBottomLeft(e->pos() + QPoint(+m_margin, -m_margin));
m_ur->setGeometry(newGeo);
newGeo = m_ll->geometry();
newGeo.setTopRight(e->pos() + QPoint(-m_margin, + m_margin));
m_ll->setGeometry(newGeo);
newGeo = m_lr->geometry();
newGeo.setTopLeft(e->pos() + QPoint(+m_margin, + m_margin));
m_lr->setGeometry(newGeo);
}
}
void FourWaySplitter::mousePressEvent(QMouseEvent * e)
{
if((e->pos() - m_handleCenter).manhattanLength() < 10) {
m_mouseMove = true;
}
}
void FourWaySplitter::mouseReleaseEvent(QMouseEvent * e)
{
m_handleCenter = rect().center();
m_mouseMove = false;
}
FourWaySplitter::~FourWaySplitter()
{
delete ui;
}
Have you tried connecting the splitterMoved(int,int) signal of one to the moveSplitter(int,int) slot of the other?
QObject::connect(ui->upperSplitter, SIGNAL(splitterMoved(int,int), ui->lowerSplitter, SLOT(moveSplitter(int,int));
QObject::connect(ui->lowerSplitter, SIGNAL(splitterMoved(int,int), ui->upperSplitter, SLOT(moveSplitter(int,int));
http://doc.qt.io/qt-5/qsplitter.html#splitterMoved
http://doc.qt.io/qt-5/qsplitter.html#moveSplitter
Or you may have to look at the QSplitterHandle class.
http://doc.qt.io/qt-5/qsplitterhandle.html
Hope that helps.

JavaFX Stage open on specific Screen - weird behavior

I open a stage using following code:
ObservableList<Screen> screens = Screen.getScreens();
if(screens.size()>1){
Rectangle2D rec = screens.get(preferredScreen).getVisualBounds();
stage.setX(rec.getMinX());
stage.setY(rec.getMinY());
}
When this is called from a window at primary screen, this works perfectly.
When this is called from a non-primary screen, the stage opens on preferred screen and then jumps to the screen where the mouse is in.
Seems to me being a Windows "feature". What would be the best way get this window to the right screen?
According to the JavaDoc of Screen, you should set the new stage relative to the primary screen, because you only extend your desktop. So you should write something like this:
int preferredScreen = 1;
double offsetX = 0;
double offsetY = 0;
Rectangle2D primaryScreenBounds = Screen.getPrimary().getVisualBounds();
ObservableList<Screen> screens = Screen.getScreens();
if(screens.size()>1){
Rectangle2D rec = screens.get(preferredScreen).getVisualBounds();
// upper left corner of the extended screen
offsetX = rec.getMinX();
offsetY = rec.getMinY();
// set relative to this screen
stage.setX(primaryScreenBounds.getMinX() + offsetX);
stage.setY(primaryScreenBounds.getMinY() + offsetY);
}
Can't test it by myself, I only have one monitor.

QPainter DrawImage CenterAligned

Is there any way to draw an image on QPainter center aligned? I see QPainter::drawText gives us this provision but drawImage does not. I have one source rect, target rect and an image. when the source size is small the image gets drawn on the left side of the page. I want it to be printed center aligned.
The painter doesn't have a size, but the device() it paints on does. You can use QRect(painter.device()->width(), painter.device()->height()) as the rectangle where you want to center your image in.
Then you'd paint the image centered like so:
QImage source;
QPainter painter(...);
...
QRect rect(source.rect());
QRect devRect(0, 0, painter.device()->width(), painter.device()->height());
rect.moveCenter(devRect.center());
painter.drawImage(rect.topLeft(), source);
I would try to do the following (please follow the source code comments):
The sample image that should be drawn
// The image to draw - blue rectangle 100x100.
QImage img(100, 100, QImage::Format_ARGB32);
img.fill(Qt::blue);
In the paint event handler
[..]
QRect source(0, 0, 100, 100);
QRect target(0, 0, 400, 400);
// Calculate the point, where the image should be displayed.
// The center of source rect. should be in the center of target rect.
int deltaX = target.width() - source.width();
int deltaY = target.height() - source.height();
// Just apply coordinates transformation to draw where we need.
painter.translate(deltaX / 2, deltaY / 2);
painter.drawImage(source, img);
Of course you should check whether source rectangle is smaller than the target before applying this approach. I omitted that code for simplicity reasons just to demonstrate how you can center your image.
I wanted to show a more complete example with a variable image size that stays within the bounds of the area provided to add to the other great answers.
void ImageView::paintEvent(QPaintEvent*)
{
if (this->imageBuffer.empty()){ return; }
double widgetWidth = this->width();
double widgetHeight = this->height();
QRectF target(0, 0, widgetWidth, widgetHeight);
QImage tempQImage = *this->imageBuffer.at(this->imageBuffer.count()-1);
tempQImage = tempQImage.scaled(rect().size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
double imageSizeWidth = static_cast<double>(tempQImage.width());
double imageSizeHeight = static_cast<double>(tempQImage.height());
QRectF source(0.0, 0.0, imageSizeWidth, imageSizeHeight);
int deltaX = 0;
int deltaY = 0;
if(source.width() < target.width())
deltaX = target.width() - source.width();
else
deltaX = source.width() - target.width();
if(source.height() < target.height())
deltaY = target.height() - source.height();
else
deltaY = source.height() - target.height();
QPainter painter(this);
painter.translate(deltaX / 2, deltaY / 2);
painter.drawImage(source, tempQImage);
}

Resources