How to properly paint content on a wxScrolledWindow? - c++

I am creating an animation timeline widget using wxWidgets.
The widget consist of a wxFrame with a wxScrolledWindow child:
I want to support horizontal zooming by changing the timeline window virtual size when the user rolls mouse middle button.
Zooming without scrolling works well. But when i start scrolling(forward and backward) things get messed up.
So to troubleshoot I decided to do a simple test:
Draw a line from the window top left corner to the bottom right corner. And the test fails!
Without scrolling -> Test pass
Zoom then scroll to right -> Test fails
Scroll back to left -> Test also fails
I have no idea of whats going on. Here my CPP code:
In .H:
class AnimationFrame : public wxFrame
{
public:
AnimationFrame();
private:
void onTimelineMouseWheel(wxMouseEvent& ev);
void onTimelinePaint(wxPaintEvent& ev);
// The scrolled window.
wxScrolledWindow* m_timelineWindow;
};
In .CPP:
AnimationFrame::AnimationFrame()
{
assert(wxXmlResource::Get()->LoadFrame(this, NULL, "AnimationFrame"));
m_timelineWindow = (wxScrolledWindow*)wxWindow::FindWindowByName("TimelineScrolledWindow", this);
assert(m_timelineWindow);
wxPanel* headerPanel = (wxPanel*)wxWindow::FindWindowByName("HeaderPanel", this);
assert(headerPanel);
// Adjust panel sizes.
{
headerPanel->SetMaxSize(wxSize(headerPanel->GetMaxSize().x, 40));
m_timelineWindow->SetMaxSize(wxSize(m_timelineWindow->GetMaxSize().x, 100));
this->SetMinSize(wxSize(this->GetSize().x, 250));
this->SetSize(wxSize(this->GetSize().x, 250));
this->SetMaxSize(wxSize(this->GetSize().x, 250));
}
// Timeline events from the scrolled window.
m_timelineWindow->Bind(wxEVT_MOUSEWHEEL, &AnimationFrame::onTimelineMouseWheel, this);
m_timelineWindow->Bind(wxEVT_PAINT, std::bind(&AnimationFrame::onTimelinePaint, this, std::placeholders::_1));
}
void AnimationFrame::onTimelineMouseWheel(wxMouseEvent& ev)
{
static const nbFloat32 scrollIntensity = 1.20f;
const nbFloat32 delta = scrollIntensity * (nbFloat32)(ev.GetWheelRotation() / ev.GetWheelDelta());
const nbFloat32 sizeFactor = delta >= 0.0 ? delta : 1.0f / -delta;
const wxSize currentVirtualSize = m_timelineWindow->GetVirtualSize();
wxSize targetVirtualSize = wxSize((nbUint32)((nbFloat32)currentVirtualSize.x * sizeFactor), currentVirtualSize.y);
targetVirtualSize.x = std::min(targetVirtualSize.x, 5000);
targetVirtualSize.x = std::max(targetVirtualSize.x, m_timelineWindow->GetSize().x);
m_timelineWindow->SetVirtualSize(targetVirtualSize);
this->Refresh();
}
void AnimationFrame::onTimelinePaint(wxPaintEvent& ev)
{
// Init the DC.
wxPaintDC dc(m_timelineWindow);
PrepareDC(dc);// from the scrolling sample. Doesnt seems to do anything.
/*
wxFont font(12, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL);
dc.SetFont(font);
dc.SetBackgroundMode(wxTRANSPARENT);
dc.SetTextForeground(*wxBLACK);
dc.SetTextBackground(*wxWHITE);
*/
// Now draw line.
dc.DrawLine(wxPoint(0, 0), wxPoint(m_timelineWindow->GetSize().x, m_timelineWindow->GetSize().y));
}
Addional information:
As you certainly realised the widget is loaded from CPP using the XRC format. Here the generated code from wxFormBuilder for the widget.
XRC: https://1drv.ms/u/s!Ak_u4fVrMHMkrTe59Fuo4X2b-fRO?e=2Z0lw0
CPP: https://1drv.ms/u/s!Ak_u4fVrMHMkrTi_8w5UIkwFTl9t?e=BZBlLs

Related

Resizing Borderless wxFrame in wxWidgets

Hi i have a borderless wxFrame but it is not resizable. I want to make the frame resizable just like when it is resizable with a border. I am using wxRESIZE_BORDER and wxBORDER_NONE style properties. I know i have to catch the mouse event manually and then implement it but am not able do that. I have also looked at the shaped sample directory but there also there is no "border-less resizable frame". I am using Ubuntu 18.04 and wxWidgets 3.1.5. Is this doable/possible in wxWidgets and is there any example of the same?
Here's an example of the basic machinery needed to allow a frame to be resized without using a resize border. It basically shows Igor's comments above.
#include "wx/wx.h"
#include <wx/timer.h>
#include <wx/dcbuffer.h>
#include <wx/version.h>
#if __WXGTK__
#define BORDERLESS_FRAME_STYLE (wxCAPTION | wxCLOSE_BOX)
#else
#define BORDERLESS_FRAME_STYLE (wxCAPTION | wxCLOSE_BOX | wxBORDER_NONE)
#endif // __WXGTK__
class MyFrame: public wxFrame
{
public:
MyFrame();
private:
enum ResizeMode
{
ResizeNone,
ResizeFromTop,
ResizeFromUpperLeft,
ResizeFromLeft,
ResizeFromLowerLeft,
ResizeFromBottom,
ResizeFromLowerRight,
ResizeFromRight,
ResizeFromUpperRight
};
// General event handlers
void OnBgPanelPaint(wxPaintEvent&);
// Event handlers for resizing
void OnLeftDownForResizeFromLowerRight(wxMouseEvent&);
void OnLeftDownForResizeFromLowerLeft(wxMouseEvent&);
void OnLeftUp(wxMouseEvent&);
void OnMouseCaptureLost(wxMouseCaptureLostEvent&);
void OnResizeTimer(wxTimerEvent&);
// Resizing helper functions
void DoDragBasedResize();
void StartResize(wxWindow*, const wxPoint&);
void CompleteResize(bool doFinalResize = false);
// Data and objects needed for resizing.
wxTimer m_resizeTimer;
int m_resizeFrequency;
int m_resizeAreaLength;
ResizeMode m_resizeMode;
wxPoint m_resizeStartPoint;
wxSize m_initialFrameSize;
wxPoint m_initialFramePosition;
wxWindow* m_clickToResizeFromLowerRightWindow;
wxWindow* m_clickToResizeFromLowerLeftWindow;
wxWindow* m_curResizeWindow;
// GUI controls
wxPanel* m_bgPanel;
};
MyFrame::MyFrame():wxFrame(NULL, wxID_ANY, "Resizing Demo", wxDefaultPosition,
wxSize(400, 300), BORDERLESS_FRAME_STYLE)
{
// Set up the UI.
m_bgPanel = new wxPanel(this, wxID_ANY, wxDefaultPosition, wxDefaultSize,
wxTAB_TRAVERSAL|wxFULL_REPAINT_ON_RESIZE);
m_bgPanel->SetBackgroundStyle(wxBG_STYLE_PAINT );
m_bgPanel->Bind(wxEVT_PAINT, &MyFrame::OnBgPanelPaint, this);
wxSizer* mainSizer = new wxBoxSizer(wxVERTICAL);
mainSizer->Add(m_bgPanel, wxSizerFlags(1).Expand());
SetSizer(mainSizer);
Layout();
// Initialize the data needed for resizing.
m_resizeMode = ResizeNone;
#if wxCHECK_VERSION(3,1,0)
m_resizeAreaLength = FromDIP(20);
#else
m_resizeAreaLength = 20;
#endif // wxCHECK_VERSION
m_resizeTimer.Bind(wxEVT_TIMER, &MyFrame::OnResizeTimer, this);
m_resizeFrequency = 50;
m_curResizeWindow = NULL;
// Set window and event handlers for resizing from lower right.
m_clickToResizeFromLowerRightWindow = m_bgPanel;
m_clickToResizeFromLowerRightWindow->Bind(wxEVT_LEFT_DOWN,
&MyFrame::OnLeftDownForResizeFromLowerRight, this);
m_clickToResizeFromLowerRightWindow->Bind(wxEVT_LEFT_UP,
&MyFrame::OnLeftUp, this);
m_clickToResizeFromLowerRightWindow->Bind(wxEVT_MOUSE_CAPTURE_LOST,
&MyFrame::OnMouseCaptureLost, this);
// Set window and event handlers for resizing from lower left.
m_clickToResizeFromLowerLeftWindow = m_bgPanel;
m_clickToResizeFromLowerLeftWindow->Bind(wxEVT_LEFT_DOWN,
&MyFrame::OnLeftDownForResizeFromLowerLeft, this);
m_clickToResizeFromLowerLeftWindow->Bind(wxEVT_LEFT_UP,
&MyFrame::OnLeftUp, this);
m_clickToResizeFromLowerLeftWindow->Bind(wxEVT_MOUSE_CAPTURE_LOST,
&MyFrame::OnMouseCaptureLost, this);
}
void MyFrame::OnLeftDownForResizeFromLowerLeft(wxMouseEvent& event)
{
wxPoint p = event.GetPosition();
wxSize sz = m_clickToResizeFromLowerLeftWindow->GetClientSize();
// Check if the click is in the lower left of the window.
if ( p.x < m_resizeAreaLength &&
sz.GetHeight() - p.y < m_resizeAreaLength )
{
StartResize(m_clickToResizeFromLowerLeftWindow, p);
m_resizeMode = ResizeFromLowerLeft;
SetTitle("Resize From lower left in progress...");
SetCursor(wxCursor(wxCURSOR_SIZENESW));
}
else
{
event.Skip();
}
}
void MyFrame::OnLeftDownForResizeFromLowerRight(wxMouseEvent& event)
{
wxPoint p = event.GetPosition();
wxSize sz = m_clickToResizeFromLowerRightWindow->GetClientSize();
// Check if the click is in the lower right of the window.
if ( sz.GetWidth() - p.x < m_resizeAreaLength &&
sz.GetHeight() - p.y < m_resizeAreaLength )
{
StartResize(m_clickToResizeFromLowerRightWindow, p);
m_resizeMode = ResizeFromLowerRight;
SetTitle("Resize from lower right in progress...");
SetCursor(wxCursor(wxCURSOR_SIZENWSE));
}
else
{
event.Skip();
}
}
void MyFrame::OnLeftUp(wxMouseEvent& event)
{
if ( m_resizeMode != ResizeNone )
{
CompleteResize(true);
}
else
{
event.Skip();
}
}
void MyFrame::OnMouseCaptureLost(wxMouseCaptureLostEvent&)
{
if ( m_resizeMode != ResizeNone )
{
CompleteResize(false);
}
}
void MyFrame::OnResizeTimer(wxTimerEvent&)
{
DoDragBasedResize();
}
void MyFrame::DoDragBasedResize()
{
wxMouseState ms = ::wxGetMouseState();
wxPoint curMousePsn = ms.GetPosition();
wxPoint dragVector = curMousePsn - m_resizeStartPoint;
wxSize newSize(m_initialFrameSize);
wxPoint newPsn(m_initialFramePosition);
if ( m_resizeMode == ResizeFromLowerRight )
{
newSize = wxSize(m_initialFrameSize.GetWidth() + dragVector.x,
m_initialFrameSize.GetHeight() + dragVector.y);
}
else if ( m_resizeMode == ResizeFromLowerLeft )
{
newSize = wxSize(m_initialFrameSize.GetWidth() - dragVector.x,
m_initialFrameSize.GetHeight() + dragVector.y);
newPsn = wxPoint(m_initialFramePosition.x + dragVector.x,
m_initialFramePosition.y);
}
SetSize(newPsn.x, newPsn.y, newSize.GetWidth(), newSize.GetHeight());
}
void MyFrame::StartResize(wxWindow* win, const wxPoint& p)
{
m_curResizeWindow = win;
m_resizeTimer.Start(m_resizeFrequency);
m_resizeStartPoint = m_curResizeWindow->ClientToScreen(p);
m_curResizeWindow->CaptureMouse();
m_initialFramePosition = GetPosition();
m_initialFrameSize = GetSize();
}
void MyFrame::CompleteResize(bool doFinalResize)
{
if ( doFinalResize )
{
DoDragBasedResize();
}
m_resizeTimer.Stop();
m_resizeMode = ResizeNone;
SetTitle("Resize complete");
SetCursor(wxCursor(wxCURSOR_ARROW));
if ( m_curResizeWindow && m_curResizeWindow->HasCapture() )
{
m_curResizeWindow->ReleaseMouse();
}
m_curResizeWindow = NULL;
}
void MyFrame::OnBgPanelPaint(wxPaintEvent&)
{
wxAutoBufferedPaintDC dc(m_bgPanel);
dc.Clear();
wxPen pen(*wxRED,5);
dc.SetPen(pen);
// Draw some red marks to indicate the lower right resize area
wxPoint lowerRight = m_bgPanel->GetClientRect().GetBottomRight();
dc.DrawLine(lowerRight - wxPoint(0,m_resizeAreaLength), lowerRight);
dc.DrawLine(lowerRight, lowerRight - wxPoint(m_resizeAreaLength,0));
// Draw some red marks to indicate the lower left resize area
wxPoint lowerLeft = m_bgPanel->GetClientRect().GetBottomLeft();
dc.DrawLine(lowerLeft - wxPoint(0,m_resizeAreaLength), lowerLeft);
dc.DrawLine(lowerLeft, lowerLeft + wxPoint(m_resizeAreaLength,0));
}
class MyApp : public wxApp
{
public:
virtual bool OnInit()
{
MyFrame* frame = new MyFrame();
frame->Show();
return true;
}
};
wxIMPLEMENT_APP(MyApp);
This only shows resizing if the click is in the lower right corner of a panel covering the client area of the frame. I tried to add enough generality that if you have another control covering the lower right, you should be able to set that control to be m_clickToResizeWindow in the code above.
There is one case where that might not work however. I could be wrong, but I think some native controls completely consume mouse clicks and won't even generate a wxMouseEvent. In this case, resizing won't be possible if such a control is in the lower right.
There are some parameters that can be changed to modify the resizing behavior. The m_resizeAreaLength determines how close to the edge a click can be to start the resizing process. I've set this to 20 DIPs. The m_resizeFrequency determines how frequently the size is updated during a resize operation. I've set this to 50ms. A smaller value will provide smoother updates. In this example, I drew some red marks on the lower right to indicate the resizing area. This is completely unnecessary and can be removed.
This example only shows resizing based on a click on the lower right. However it shouldn't be too hard to modify the code to allow only horizontal resizing based on a click on the right edge or vertical resizing based on a click on the bottom edge. However, it might get complicated if there are multiple controls covering the left or bottom edges. This system only works well if there is one control covering those edges.
edit: I've updated the code to show resizing from both the lower left and lower right while keeping the rest of the frame in the appropriate position.
This can be further expanded to allow for resizing from other edges or corners. To do so it will be necessary to:
add a pointer for the window that will be used to receive the click starting the resize.
add a left down handler for that window to start the process.
Bind the new left down handler, the existing left up handler, and the existing capture lost handler for the window.
add to the DoDragBasedResize method setting a new size and position for the frame appropriate for the type of resizing being done.

How to grab pointer to popup dialog in Qt which blocks the UI thread in QTest

I'm writing an integration test using QTest in which as soon as I click into the viewport of the widget, a multiline popup QInputDialog appears which blocks the further execution of code and requires manually canceling the dialog. Here is the code:
void PartTest::testTypewriterAnnotTool()
{
Okular::Part part(nullptr, nullptr, QVariantList()); // It is the main widget of PDF reader comprising of viewport where PDF page is shown
part.openUrl(QUrl::fromLocalFile(QStringLiteral(KDESRCDIR "data/file1.pdf"))); // open file1.pdf
part.widget()->show();
QVERIFY(QTest::qWaitForWindowExposed(part.widget()));
// Width and height of pageView widget, the child of Part widget which shows the PDF page
const int width = part.m_pageView->horizontalScrollBar()->maximum() +
part.m_pageView->viewport()->width();
const int height = part.m_pageView->verticalScrollBar()->maximum() +
part.m_pageView->viewport()->height();
part.m_document->setViewportPage(0); // sets viewport page 0, i.e. page number 1
QMetaObject::invokeMethod(part.m_pageView, "slotToggleAnnotator", Q_ARG( bool, true )); // toggles and shows the annotation toolbar with all tools avaialable
QList<QToolButton *> toolbuttonList = part.m_pageView->findChildren<QToolButton *>(); // find a list of annotation toolbutton
// Check if the tooltip of 10th button is "Typewriter"
QToolButton* typewriterButton = toolbuttonList.at(9);
QCOMPARE( typewriterButton->toolTip(), QStringLiteral("Typewriter") );
typewriterButton->click(); // clicking and selecting typewriter annotation tool
QTest::mouseMove(part.m_pageView->viewport(), QPoint(width * 0.5, height * 0.2)); // leading mouse pointer to a specific point in the viewport
QTest::mouseClick(part.m_pageView->viewport(), Qt::LeftButton, Qt::NoModifier, QPoint(width * 0.5, height * 0.2)); // mouse left button click on the viewport and a popup dialog with 'add a new note' appears
}
I have to write a test case where I have to grab that popup dialog and programmatically close it. I'm trying to achieve so by using the QTimer where I will execute a function to grab the dialog after 1 second of clicking in the viewport and then trying to close it like this:
class PartTest
{
...
private:
Okular::part *m_part;
}
void PartTest::testTypewriterAnnotTool()
{
...
m_part = ∂
QTimer* mTimer = new QTimer(this);
mTimer->setSingleShot(true);
connect(mTimer, SIGNAL(timeout()), SLOT(testDialogClosed()));
mTimer->start(1000);
QTest::mouseClick(part.m_pageView->viewport(), Qt::LeftButton, Qt::NoModifier, QPoint(width * 0.5, height * 0.2));
}
void PartTest::testDialogClosed()
{
bool m_clicked = false;
QDialog *dialog = m_part->widget()->findChild<QDialog*>();
if (dialog)
{
QDialogButtonBox *buttonBox = dialog->findChild<QDialogButtonBox*>();
buttonBox->button(QDialogButtonBox::Cancel)->click();
m_clicked = true;
}
QVERIFY(m_clicked);
}
Here, QVERIFY(m_clicked) test case always "fail" that means the popup QInputDialog is not the child of Okular::Part and I'm not getting a way to grab the pointer to this dialog window and close it. Any help?
You can enumerate the top-level widgets and find the dialog that way:
template <class P> QList<P> topLevelWidgets() {
QList<P> widgets
for (auto *w : QApplication::topLevelWidgets())
if (auto *t = qobject_cast<P>(w))
widgets << t;
return widgets;
}
template <class P> P topLevelWidget() {
auto w = topLevelWidgets<P>();
return (w.size() == 1) ? w.first() : nullptr;
}
void PartTest::testDialogClosed()
{
bool m_clicked = false;
if (auto *dialog = topLevelWidget<QDialog*>())
if (auto *buttonBox = dialog->findChild<QDialogButtonBox*>()) {
buttonBox->button(QDialogButtonBox::Cancel)->click();
m_clicked = true;
}
QVERIFY(m_clicked);
}

Programmatically invoke Snap/Aero maximize

Is there a way to programmatically invoke the Aera maximize effect using C or C++ for a specific window/window ID?
For example:
or
(source: thebuzzmedia.com)
I am using a border-less Qt window and Qt has an API for getting the window ID. I want to programmatically trigger the windows effects without the known triggers.
I don't want to talk about every single detail involved in achieving this effect, not only there's a lot that goes on but you also mentioned you understand the logic to place the windows at their specific locations. In this answer I'll address what I believe are the 2 main challenges:
How to receive and handle a maximize event?
How to create an approximation of the aero snap effect?
In order to answer the first question, we must analyze which event handlers are triggered when the window is maximized:
void resizeEvent(QResizeEvent* evt); // Invoked first,
void paintEvent(QPaintEvent* event); // then second,
void changeEvent(QEvent* evt); // and at last.
A Qt application is first notified of a resizeEvent(), which is followed by a paintEvent() to draw the window (or widget), and only after everything has been displayed, changeEvent() is invoked to let you know the widget was maximized (maybe it's a little bit late to receive such notification, I don't know).
Of all these, the only one we care about is resizeEvent(). This event handler informs the new window/widget size that can be used for comparison with the desktop size, thus allowing us to know if the event was actually a maximize request. Once we identify a maximize request, we can figure out whether the application should be maximized (and anchored) to right, left or to the center of the screen.
This would be the time to create the aero snap widget and place it on the screen as a visual clue to the user.
To answer the second question, I don't think is possible to call the native Windows API and ask it politely to perform this effect on your window. The only other logical choice is to write a code that approximates this effect ourselves.
The visual appearance can be replicated by drawing a transparent window with a shadow-ish border. The approach demonstrated in the source code below, creates and customizes a QWidget to make it behave and look like a aero snap window:
It's not the most beautiful thing in the world, I know. This demo creates a regular window for the user to interact with, and once it's maximized, it places itself to the left of the screen. To the right size of the screen it displays something that resembles an aero snap window (shown above).
The idea behind the aero snap widget is very simple: a QWidget with transparent background and a custom painting procedure. In other words, it's a transparent window which draws a rounded rectangle with a shadow and that's it.
To make it a bit more realistic, you should add some animation to resize the widget little by little. A for loop might do the trick, but if you need something fancy you'll end up using timers. If you take a look here, you can see the quickest & dirtiest method to perform animation with Qt in action, and better ways to deal with animation. However, for simple tasks like this, stick with frame-based animation.
main.cpp:
#include "window.h"
#include <QApplication>
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
Window window;
window.show();
return app.exec();
}
window.h:
#pragma once
#include "snapwindow.h"
#include <QMainWindow>
#include <QEvent>
class Window : public QMainWindow
{
public:
Window();
void resizeEvent(QResizeEvent* evt);
//void paintEvent(QPaintEvent* event);
void changeEvent(QEvent* evt);
private:
SnapWindow* _sw;
};
window.cpp:
#include "window.h"
#include "snapwindow.h"
#include <QDebug>
#include <QWindowStateChangeEvent>
#include <QApplication>
#include <QDesktopWidget>
Window::Window()
{
setWindowTitle("AeroSnap");
resize(300, 300);
_sw = new SnapWindow(this);
_sw->hide();
}
void Window::changeEvent(QEvent* evt)
{
if (evt->type() == QEvent::WindowStateChange)
{
QWindowStateChangeEvent* event = static_cast<QWindowStateChangeEvent*>(evt);
if (event->oldState() == Qt::WindowNoState &&
windowState() == Qt::WindowMaximized)
{
qDebug() << "changeEvent: window is now maximized!";
}
}
}
// resizeEvent is triggered before window_maximized event
void Window::resizeEvent(QResizeEvent* evt)
{
qDebug() << "resizeEvent: request to resize window to: " << evt->size();
QSize desktop_sz = QApplication::desktop()->size();
//qDebug() << "resizeEvent: desktop sz " << desktop_sz.width() << "x" << desktop_sz.height();
// Apparently, the maximum size a window can have in my system (1920x1080)
// is actually 1920x990. I suspect this happens because the taskbar has 90px of height:
desktop_sz.setHeight(desktop_sz.height() - 90);
// If this not a request to maximize the window, don't do anything crazy.
if (desktop_sz.width() != evt->size().width() ||
desktop_sz.height() != evt->size().height())
return;
// Alright, now we known it's a maximize request:
qDebug() << "resizeEvent: maximize this window to the left";
// so we update the window geometry (i.e. size and position)
// to what we think it's appropriate: half width to the left
int new_width = evt->size().width();
int new_height = evt->size().height();
int x_offset = 10;
setGeometry(x_offset, 45, new_width/2, new_height-45); // y 45 and height -45 are due to the 90px problem
/* Draw aero snap widget */
_sw->setGeometry(new_width/2-x_offset, 0, new_width/2, new_height);
_sw->show();
// paintEvent() will be called automatically after this method ends,
// and will draw this window with the appropriate geometry.
}
snapwindow.h:
#pragma once
#include <QWidget>
class SnapWindow : public QWidget
{
public:
SnapWindow(QWidget* parent = 0);
void paintEvent(QPaintEvent *event);
};
snapwindow.cpp:
#include "snapwindow.h"
#include <QPainter>
#include <QGraphicsDropShadowEffect>
SnapWindow::SnapWindow(QWidget* parent)
: QWidget(parent)
{
// Set this widget as top-level (i.e. owned by user)
setParent(0);
/* Behold: the magic of creating transparent windows */
setWindowFlags(Qt::Widget | Qt::FramelessWindowHint);
setStyleSheet("background:transparent;");
setAttribute(Qt::WA_NoSystemBackground, true); // speed up drawing by removing unnecessary background initialization
setAttribute(Qt::WA_TranslucentBackground);
//setAutoFillBackground(true);
/* Use Qt tricks to paint stuff with shadows */
QGraphicsDropShadowEffect* effect = new QGraphicsDropShadowEffect();
effect->setBlurRadius(12);
effect->setOffset(0);
effect->setColor(QColor(0, 0, 0, 255));
setGraphicsEffect(effect);
}
void SnapWindow::paintEvent(QPaintEvent *event)
{
QWidget::paintEvent(event);
/* Lazy way of painting a shadow */
QPainter painter(this);
QPen pen(QColor(180, 180, 180, 200));
pen.setWidth(3);
painter.setPen(pen);
// Offset 6 and 9 pixels so the shadow shows up properly
painter.drawRoundedRect(QRect(6, 6, (width()-1)-9, (height()-1)-9), 18, 18);
}
This is just a quick demo to point you to the right direction. It is by no means a complete implementation of the effect you are looking for.
Maybe it is not what you need, but this effect is just resizing and moving window then try use Qt methods to do this.
bool left = false;
QSize size = QApplication::desktop()->size();//resolution of current screen
if(left)
{//left side
this->setGeometry(0, 0, size.width()/2, size.height());//(maybe need do some changes)
}
else
{//right side
this->setGeometry(size.width()/2, 0, size.width()/2, size.height());
}
With QApplication::desktop() it will work properly on screen with different resolutions.
In web I found something similar in winapi, but it didn't work properly:
HWND act = GetForegroundWindow();
PostMessage((HWND)act,WM_NCLBUTTONDBLCLK, HTTOP, 0);
The best way
Combine this approaches. For example:
HWND act = GetForegroundWindow();
bool left = false;
QSize size = QApplication::desktop()->size();
if(left)
{
this->move(0,0);
PostMessage((HWND)act,WM_NCLBUTTONDBLCLK, HTTOP, 0);
this->resize(size.width()/2,QApplication::desktop()->height());
}
else
{
this->move(size.width()/2,0);
PostMessage((HWND)act,WM_NCLBUTTONDBLCLK, HTTOP, 0);
this->resize(size.width()/2,QApplication::desktop()->height());
}
Why? Because move() regulate left and right sides, but PostMessage (winapi) set window's height properly on every screen (window will not locate lower then taskbar, as in your example)
EDIT
I changed code a little and now it is better. Yes, it is resizing again, but now it hasn't winapi code (PostMessage etc), so Photoshop doesn't catch it, there is one interesting method in Qt which called availableGeometry. It return normal height of screen which we need, with this method borderless windows perfectly simulates Aero Snap effects in different directions. It is works, maybe don't so good, but as I can see, there isn't API for Aero effects. Maybe this approach will be normal for yoo.
There is Aero Peek in Qt : http://qt-project.org/doc/qt-5/qtwinextras-overview.html , but it is can't solve this problem too.
Code:
bool left = true;
bool upper = true;
if(upper)
{
QRect rect = QApplication::desktop()->availableGeometry(-1);
this->setGeometry(rect);
}
else if(left)
{
QRect rect = QApplication::desktop()->availableGeometry(-1);
rect.setWidth(rect.width()/2);
this->setGeometry(rect);
}
else
{
QRect rect = QApplication::desktop()->availableGeometry(-1);
int half = rect.width()/2;
rect.setX(half);
rect.setWidth(half);
this->setGeometry(rect);
}
Try it with frameless window! You should choose one direction or let user choose it.

Repositioning a label with mouseevents to anywhere on the screen

I've created a label and used setPixmap to attach a .png image to the label. I've also setWindowFlags to disable the title bar and create a frameless window. Because I've disabled those, it also disables the ability to drag anything around, so I want to create mouseevents (unless there's a better method) to position my label anywhere on the screen exactly like dragging the frame of a window. How would I do that? An example and brief explanation would be greatly appreciated.
reimplement the QMouseEvents you need .... like
void MyLabel::mousePressEvent(QMouseEvent* e)
{
m_moveDatWidget = true;
// so when the mousebutton got pressed, you set something to
// tell your code to move the widget ... consider maybe you
// want to move it only on right button pressed ...
}
void MyLabel::mouseReleaseEvent(QMouseEvent* e)
{
m_moveDatWidget = false;
// after releasing do not forget to reset the movement variable
}
void MyLabel::mouseMoveEvent(QMouseEvent* e)
{
// when in 'moving state' ...
if (m_moveDatWidget)
{
// move your widget around using QWidget::move(qreal,qreal)
}
}
this is only a really basic implementation but it should do well if you calculate the desired movement correct :)
I would implement the label dragging by mouse in the following way:
class Label : public QLabel
{
public:
// Implement the constructor(s)
protected:
void Label::mouseMoveEvent(QMouseEvent* event)
{
if (!m_offset.isNull()) {
move(event->globalPos() - m_offset);
}
QLabel::mouseMoveEvent(event);
}
void Label::mousePressEvent(QMouseEvent* event)
{
// Get the mouse offset in the label's coordinates system.
m_offset = event->globalPos() - pos();
QLabel::mousePressEvent(event);
}
void Notifier::mouseReleaseEvent(QMouseEvent* event)
{
m_offset = QPoint();
QLabel::mouseReleaseEvent(event);
}
private:
// The mouse pointer offset from the top left corner of the label.
QPoint m_offset;
};

Remove scroll functionality on mouse wheel QGraphics view

I have a QGraphicsView window on my widget and have just put in an event for mouse wheel which zooms in on the image.
However as soon as i zoom in scroll bars are displayed and the scroll functionality on the mouse wheel overrides the zoom function i have.
i was wondering if there is any way that i can remove scrolling all together and add a drag to move option or maybe a CTRL and mouse wheel to zoom and mouse wheel alone would control scrolling
here is my zoom function (Which im aware isnt perfect) but if anyone could shed some light on that it would be a bonus
cheers in advance
void Test::wheelEvent(QWheelEvent *event)
{
if(event->delta() > 0)
{
ui->graphicsView->scale(2,2);
}
else
{
ui->graphicsView->scale(0.5,0.5);
}
}
You reimplemented wheelEvent for QWidget/QMainWindow that contains your QGraphicsView, however, wheelEvent of QGraphicsView remains intact.
You can derive from QGraphicsView, reimplement wheelEvent for derived class and use derive class instead of QGraphicsView - this way you won't even need wheelEvent in your QWidget/QMainWindow, and you can customize reimplemented wheelEvent to do what you want. Something like that:
Header file:
class myQGraphicsView : public QGraphicsView
{
public:
myQGraphicsView(QWidget * parent = nullptr);
myQGraphicsView(QGraphicsScene * scene, QWidget * parent = nullptr);
protected:
virtual void wheelEvent(QWheelEvent * event);
};
Source file:
myQGraphicsView::myQGraphicsView(QWidget * parent)
: QGraphicsView(parent) {}
myQGraphicsView::myQGraphicsView(QGraphicsScene * scene, QWidget * parent)
: QGraphicsView(scene, parent) {}
void myQGraphicsView::wheelEvent(QWheelEvent * event)
{
// your functionality, for example:
// if ctrl pressed, use original functionality
if (event->modifiers() & Qt::ControlModifier)
{
QGraphicsView::wheelEvent(event);
}
// otherwise, do yours
else
{
if (event->delta() > 0)
{
scale(2, 2);
}
else
{
scale(0.5, 0.5);
}
}
}
Scrolling can be disabled with the following code:
ui->graphicsView->verticalScrollBar()->blockSignals(true);
ui->graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->graphicsView->horizontalScrollBar()->blockSignals(true);
ui->graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
I think your question has a bit simpler answer.. To disable scroll bars just set scroll bar policy (QGraphicsView is just QScrollView), so step 1)
setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
that will disable scroll bars..
step 2) (if you want to keep it simple)
QGraphicsView * pView; // pointer to your graphics view
pView->setInteractive(true);
pView->setDragMode(QGraphicsView::ScrollHandDrag);
thats the fastest way to get results you want