Gtk / Gtkmm Kinetic Scrolling - c++

I am designing a GUI that requires kinetic scrolling for my application and I am using GTK (coming from Qt) and I have the following code:
Gtk::Window * wnd = new Gtk::Window();
Gtk::ScrolledWindow * scr = new Gtk::ScrolledWindow();
Gtk::Layout * lay = new Gtk::Layout();
Gtk::VBox * vbox = new Gtk::VBox();
wnd->add(*scr);
scr->add(*vbox);
for (int i = 0; i < 20; i++)
{
Gtk::Button * btn = new Gtk::Button();
btn->set_label("Click Me");
vbox->pack_start(*btn);
btn->show();
}
scr->set_kinetic_scrolling(true);
wnd->show();
scr->show();
lay->show();
vbox->show();
and this is what you get:
This link tells me the following:
void Gtk::ScrolledWindow::set_kinetic_scrolling ( bool
kinetic_scrolling = true )
Turns kinetic scrolling on or off.
Kinetic scrolling only applies to devices with source
Gdk::SOURCE_TOUCHSCREEN.
Since gtkmm 3.4:
Parameters
kinetic_scrolling true to enable kinetic scrolling.
I tried grabbing the button with the mouse and to kinetically scroll but as expected, it does not work. I don't have a touch screen yet and I need to experiment with my mouse, Is there any way I can do this?
I also found the following:
enum InputSource {
SOURCE_MOUSE,
SOURCE_PEN,
SOURCE_ERASER,
SOURCE_CURSOR,
SOURCE_KEYBOARD,
SOURCE_TOUCHSCREEN,
SOURCE_TOUCHPAD,
SOURCE_TRACKPOINT,
SOURCE_TABLET_PAD
}
An enumeration describing the type of an input device in general terms.
Is there any way I can emulate the type of device? I want to see it working.

You must have a touch event. Use GtkInspector (Ctrl+Shift+I) then in the Visual tab, set Simulate touchscreen (as shown below):
It should work. More on Gtk Inspector

Related

Jittery movement of QScrollArea from custom touchscreen driver

So a few years ago, I wrote a custom touchscreen driver specifically for a particular application which ran on a Scientific Linux 6.4 (CentOS 6 based) OS, which did not have native touch support, but the kernel supported touch events, so I was able to directly read the raw data from the touchscreen in /dev/input/event* and read the event data to generate mouse events with Qt to control the application to mimic a multi-touch touchscreen. More recently, we've finally migrated to RedHat 8.4, but I had to disable the native touch driver, because as far as I know, the native Qt touch events didn't allow the same degree of control over the application that my driver did.
Recently during a trial, a technician reported that when using the touchscreen to manipulate one of the application's image display areas, the movement was very "jittery". The image display area is simply a QScrollArea with a QImage inside, and hidden scroll bars. The way it works is that on a mouseMoveEvent, it manipulates the scrollbars according to the delta value on the mouse event.
void PanArea::mouseMoveEvent(QMouseEvent* e)
{
SafeStr str;
if (m_pw)
{
if (m_disable_panning == false &&
e->buttons().testFlag(Qt::RightButton))
{
QPoint delta = e->pos() - m_last_pos;
horizontalScrollBar()->setValue(horizontalScrollBar()->value() -
delta.x());
verticalScrollBar()->setValue(verticalScrollBar()->value() -
delta.y());
m_last_pos = e->pos();
emit signalScrollPositionChanged(getScrollPosition());
// This force update has been added because
// when fast panning cause black to appear within the image
// because some pan widget updates were being skipped.
// Performance seems acceptable with this here.
m_pw->update();
}
else if (...)
{
// irrelevant code removed to save space
}
}
}
This works fine when simply right-click dragging on the scroll area. And then here is the relevant function that dispatches the mouse press and mouse move events from the touchscreen driver:
void InputHandler::panStart(TouchPoint* tp, QWidget* widget)
{
QPoint pos(tp->cx(), tp->cy());
QWidget* target = widget;
if (target == NULL) target = QApplication::widgetAt(pos);
if (target != NULL)
{
QPoint local = target->mapFromGlobal(pos);
QMouseEvent* press =
new QMouseEvent(QEvent::MouseButtonPress, local, pos,
Qt::RightButton, Qt::RightButton, Qt::NoModifier);
QApplication::postEvent(widget, press);
}
}
void InputHandler::panMove(TouchPoint* tp, QWidget* widget)
{
QPoint pos(tp->cx(), tp->cy());
QWidget* target = widget;
if (target == NULL) target = QApplication::widgetAt(pos);
if (target != NULL)
{
QPoint local = target->mapFromGlobal(pos);
QMouseEvent* move =
new QMouseEvent(QEvent::MouseMove, local, pos, Qt::NoButton,
Qt::RightButton, Qt::NoModifier);
QApplication::postEvent(widget, move);
}
}
When I touch and drag on the widget, it DOES move, but it jumps all over the place as if I were rapidly dragging it in random directions.
Is there some caveat of how Qt5 mouse events work that could explain this behavior? Or something to do with the way the widget is moving the image around?

Qt GUI application on macOS: how to find the currently active screen?

We are developing a macOS application whose GUI is relying on Qt.
At startup, we want to show() the QMainWindow at a specific location on the currently active screen (with multi screen systems in mind).
Is there a way to get the QScreen representing the currently active screen?
From our test, QGuiApplication::primaryScreen() is the first screen (which is consistent with the name), but we cannot find an equivalent for the active screen.
Qt5 provides functionality to do so, the QWindow::setScreen method sets the screen on which the window should be shown.
Any widget provides access to this pointer via QWidget::windowHandle():
QWidget * widget = new QWidget();
auto screens = qApp->screens();
// compute the index
widget->windowHandle()->setScreen(screens[index]);
widget->showFullScreen();
To get the screen number, you can use the mouse position and assume that the screen with the mouse is the one with the current focus:
QPoint globalCursorPos = QCursor::pos();
int mouseScreen = qApp->desktop()->screenNumber(globalCursorPos);
So the final code can be something like that:
QWidget * widget = new QWidget();
const auto globalCursorPos = QCursor::pos();
const auto mouseScreen = qApp->desktop()->screenNumber(globalCursorPos);
widget->windowHandle()->setScreen(qApp->screens()[mouseScreen]);
widget->showFullScreen();
Windows
If this approach does not fit your needs, you will need to perform some OS calls.
For instance, on Windows, you can use MonitorFromWindow:
HMONITOR active_monitor_number = MonitorFromWindow(GetActiveWindow(), MONITOR_DEFAULTTONEAREST);
If you need more information about the screen, you can use Qt or GetMonitorInfo.
I am not a Mac OS X developer, but it may exist a similar API
I did it in the following way:
#if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0))
QPoint topLeft = QPoint(0, 0);
QScreen* currentScreen = QGuiApplication::screenAt(QCursor::pos());
if (nullptr != currentScreen) {
topLeft = currentScreen->geometry().topLeft();
}
#else
QPoint topLeft =
qApp->desktop()
->screenGeometry(qApp->desktop()->screenNumber(QCursor::pos()))
.topLeft();
#endif
someWidget->move(mapFromGlobal(topLeft) + QPoint(offset, offset));
Note that, sometimes, you may get nullptr for currentScreen (in my case if primary screen is at the bottom and mouse pos at the bottom or left edge on the primary screen).

Gtk Move window beyond constraints

I am currently writing a gtk program that uses a custom title bar (i.e., it is not being decorated by the window manager). But since using a custom title bar also disables support of dragging the window around, I wrote my custom drag function which moves the window by calling window.move(x, y):
bool on_titlebar_drag(GdkEvent* event)
{
static int drag_x_offset = 0;
static int drag_y_offset = 0;
int x, y;
if (event->type == GDK_BUTTON_PRESS)
{
drag_x_offset = event->button.x;
drag_y_offset = event->button.y;
} else if(event->type == GDK_BUTTON_RELEASE) {
drag_x_offset = 0;
drag_y_offset = 0;
} else if(event->type == GDK_MOTION_NOTIFY) {
x = event->motion.x_root - drag_x_offset;
y = event->motion.y_root - drag_y_offset;
mainWindow.move(x, y);
}
return true;
}
This works just fine except of the fact that it cannot move the window beyond the screen limits, like the normal behaviour for other windows, so you can drag it "out of sight" to make place for others.
I am trying to resize the window smaller as soon as it touches the screen by calling window.resize(width, height) but this is not what I intend to do, because resizing also resizes the window contents to the smaller scale, while I would just like to make its physical size smaller.
I have also tried using set_allocation and size_allocate, these two didnt make any change at all.
My question is, do you know a way to either be able to move the window beyond the screen borders (not totally, but in a way that the window is not fully on-screen), or to change the size of the window without resizing its contents?
If you are using GTK 3.10 or newer you can use gtk_window_set_titlebar
gtk_window_set_titlebar (GtkWindow *window, GtkWidget *titlebar) the only arguments you need are the window that you want to customise and the GtkWidget that will serve as the titlebar. Using GtkHeaderBar is suggested but in your case you can use any custom GtkWidget and get draggable bar which will tug the whole window as would the one from the window manager.

Make a QDialog appear in a different screen

The title says it pretty much all:
I have two screens, and each time I create a QDialog it appears in the same screen as its parent.
How can I make it appear in a different screen? Or should I use a different type of top-level widget?
The code I use to create the dialog is:
QDialog my_dialog = new QDialog(this,
Qt::WindowMaximizeButtonHint |
Qt::WindowCloseButtonHint);
...
EDIT:
I have also tried using the QDesktopWidget which gives me a QScreen object that refers to the second screen. But then I don't find how to instruct the QDialog to use that QScreen (setting it as the parent doesn't work).
It is bad, that you edit your question without reading comments :(
// Your screen geometry:
QRect buildScreenGeometry()
{
auto desktop = QApplication::desktop();
QRect virtualRect;
const auto n = desktop->screenCount();
for ( auto i = 0; i < n; i++ )
virtualRect |= desktop->screenGeometry(i);
return virtualRect;
}
// Moving
auto dlg = new QDialog( someParent );
auto newPoint = QPoint( 2000, 0 ); // point on another screen
auto realPos = someParent->mapFromGlobal( newPoint );
dlg->move( realPos );
That's all.
UPDATE:
You should understand, that there are only ONE screen area with COMMON coordinate system, that contains ALL screens.
For example, you have 2 monitors with 800x600 resolution. First (main) monitor is standing left, and second standing right. In this case, coordinate system, that is available for your application is 1600x600. So, if your widget has 100x100 top-left position, on a first monitor, and you want to move it to another, you should call move(900x100); // 900 == screen1.width() + dialog.pos().x(). Then your widget will have 100x100 position on second monitor.
You should read Qt documentation.
You can use move on your QDialog, but be aware that move will set the QDialog position relative to it's parent.
You can get the Main Window's screen position and use that to setup the QDialog position. Just know that you're not guaranteed to have 2 screens on the end user machine.
For more information on move see: http://doc.qt.io/qt-5/application-windows.html#window-geometry

How to add effects in QTDialog during show/hide?

Is there any way to add effects like dialog coming out to maximum size from tiny size when users clicks to show dialog !
like in iphoto when we request to open a dialog, it comes out in the same way!!!
The code which i'm using is :
fade_effect = new QGraphicsOpacityEffect(this);
this->setGraphicsEffect(fade_effect);
animation = new QPropertyAnimation(fade_effect, "opacity");
animation->setEasingCurve(QEasingCurve::InOutQuad);
animation->setDuration(5000);
animation->setStartValue(1);
animation->setEndValue(0.01);
animation->start(QPropertyAnimation::DeleteWhenStopped);
this->setWindowOpacity(0.5);
//this->hide();
//QDialog::reject();
Its not working in hiding case.
Qt Animation Framework gives you a lot of tools to create animation effects. Here is a sample how you can achieve your aim with QPropetyAnimation :
void YourWindowClass::showEvent(QShowEvent* e)
{
//create animation for "geometry" property
QPropertyAnimation *animation = new QPropertyAnimation(this, "geometry");
//duration in ms
animation->setDuration(500);
//starting geometry
QRect startRect(900,500,100,100);
//final geometry
QRect endRect(750,350,400,400);
animation->setStartValue(startRect);
animation->setEndValue(endRect);
//starts animation which will be deleted when finished
animation->start(QAbstractAnimation::DeleteWhenStopped);
}