How do I tell if a QPlainTextEdit has scrolled? - qt

There is a well know bug (feature?) that valueChanged signals are blocked some of the time for a scrollbar associated with a QTpainTextEdit, so I figured I'd override the scrollContentsBy() virtual function and notice scrolling that way. But scrollContentsBy is also sometimes not called. So how the devil can a program reliably notice that a QPlainTextEdit was scrolled?

You could check the current content offset with contentOffset() when you need to know whether scrolling has been changed. It's a protected method, so you need to subclass QPlainTextEdit to call it. One caveat is that if the content is smaller than the viewport, it will give back (0,0) even if the content was moved within the viewport, so at the end it depends on your definition of scrolling, but you can implement some custom logic for it, depending on your requirements.

Related

How to enable both internal reordering and external dropping in a Qt widget?

I have created a widget which inherits QListWidget.
My goal is for it to accept files dropped into it from an external file manager, and for the user to be able to reorder the elements in the widget. I can achieve both, but not at the same time.
If I just set
myWidget->setDragDropMode(QListView::InternalMove);
myWidget->setDragEnabled(true);
I can reorder the items within the widget, but I can't drop external items into it.
If I reimplement the dragMoveEvent, dragEnterEvent and dropEvent events, all of them just having acceptProposedAction(); and some debug messages inside them, I can drop external files into my widget, but I can no longer rearrange the items.
Is there a way to have the above two at the same time, or do I have to manage the items myself in the reimplemented functions? If so, how can I know if a dropped item is internal or external, and how can I know from which position it was taken and into which position in the list it was dropped into?
If I parse the mimeData which I got from the event, I can see whether it as a file or a text, and I get "qabstractitemmodeldatalist" if it was an internal item, but it still doesn't give me its position.
I can check event->pos() to know in pixels where the drop has been made, and event->source() to learn about what was dropped there, but is this really best practice, to start calculating pixel values and adding objects "manually"?
The solution was very simple: I just had to call the functions of the parent class at the end of each function I've overridden.
void myWidget::dropEvent(QDropEvent *event)
{
do_stuff_with_received_data(event);
QListWidget::dropEvent(event);
}

How to update a QLayout and get the new dimensions before returning?

This is driving me nuts. I have a custom menu class that, when set visible, shows a list of items located in a particular folder. When a hardware button is pressed, my application gets the latest list of items, populates the menu with them, and returns.
The menu displaying these items uses a QListWidget filled with custom widgets. Each of the widgets contains one or more QLabels in a horizontal layout, and is created at the time the menu is shown. In order to adjust the text displayed based on the menu width available, I need to get the size of the QLabel AFTER it has been resized according to the layout, but before the menu becomes visible to the user. The problem is, my layout does not get updated until all of the functions constructing my list return.
I have tried QApplication::ProcessEvents() and the layout update functions, but none of them have updated the values of my QLabels before returning. I can set a QTimer when the button is initially pressed, and have it show the menu, update the items, and stop itself, but that seems like a terrible solution.
Any help would really be appreciated! I've spent most of a day on this.
Marlon
I had this exact problem and could not find an answer anywhere on the Internet. Calling Layout.update(), Layout.activate(), or widget.adjustSize() (all suggested in various places) all did not work.
I had a widget with a vertical layout that I wanted to add a QLabel to and then immediately use the size of the QLabel.
The only thing that worked reliably was
layout->addWidget(myLabel);
myLabel->show();
size = myLabel->size();
It would seem that layouts will just not recalculate until you either return from a function and allow the Qt event loop to progress or manually call show() yourself.
How to update a QLayout and get the new dimensions before returning?
Don't. You're not meant to do that. It'll drive you "nuts" because you're doing it backwards. Layout updates are handled asynchronously from the event loop. Instead of getting layout dimensions right away, set yourself up to be part of the system. Some options are:
Implement a custom widget that will interact properly with the layout, growing to fill the available width of the layout. Perhaps all you need is a size policy and a way to elide text?
Make a custom layout that takes the special properties of your use case into account.
You want to call QWidget::adjustSize() on your parent widget. This will force the layout recalculations.
Have you tried using layout()->update(); ?
I've tried many but nothing works for me on Qt 5.15.
Only invented little patch - create timer and get size after 20 msec:
QTimer::singleShot(20, this, [this]
{
const auto height = myLayout->contentsRect().height();
// ...
});

How to make two side by side Qt Windows sticky and act like a single window?

I am trying to implement a scenario where two Qt windows will be placed side by side and they will be kind of sticky to each other. By dragging one of them, the other also gets dragged. Even when doing an alt-tab they should behave like a single window.
Any help or pointer will be extremely helpful.
-Soumya
What you describe sounds like it's a good fit for a "docking" scenario. You're probably most familiar with docking from toolbars; where you can either float a toolbar on its own or stick it to any edge of an app's window. But Qt has a more generalized mechanism:
http://doc.qt.io/qt-5/qtwidgets-mainwindows-dockwidgets-example.html
http://doc.qt.io/qt-5/qdockwidget.html
It won't be a case where multiple top level windows are moved around in sync with their own title bars and such. The top-level windows will be merged into a single containing window when they need to get "sticky". But IMO this is more elegant for almost any situation, and provides the properties you seem to be seeking.
Install a event filter on the tracked window with QObject::installEventFilter() and filter on QEvent::Move
You can then change the position of tracking window whenever your filter is called with that event type.
I found a way to keep two windows anchored: when the user moves a window, the other follows, keeping its relative position to the moved one.
It is a bit of a hack, because it assumes that the event QEvent::NonClientAreaMouseButtonPress is sent when the user left clicks on the title bar, holding it pressed while he moves the window, and releasing it at the end, so that QEvent::NonClientAreaMouseButtonRelease is sent.
The idea is to use the QWidget::moveEvent event handler of each window to update the geometry of the other, using QWidget::setGeometry.
But the documentation states that:
Calling setGeometry() inside resizeEvent() or moveEvent() can lead to infinite recursion.
So I needed to prevent the moveEvent handler of the windows which was not moved directly by the user, to update the geometry of the other.
I achieved this with result via QObject::installEventFilter, intercepting the summentioned events.
When the user clicks on the title bar of WindowOne to start a move operation, WindowOne::eventFilter catches its QEvent::NonClientAreaMouseButtonPress and sets the public attribute WindowTwo::skipevent_two to true.
While the user is moving WindowOne, WindowTwo::moveEvent is called upon the setGeometry operation, performed on WindowTwo from WindowOne::moveEvent.
WindowTwo::moveEvent checks WindowTwo::skipevent_two, and if it is true, returns without performing a setGeometry operation on WindowOne which would cause infinite recursion.
As soon as the user releases the left mouse button, ending the window move operation, WindowOne::eventFilter catches QEvent::NonClientAreaMouseButtonRelease and sets back the public attribute WindowTwo::skipevent_two to false.
The same actions are performed if the user clicks the titlebar of WindowTwo, this time causing WindowOne::skipevent_one attribute to be set to true and preventing WindowOne::moveEvent to perform any setGeometry operation on WindowTwo.
I believe this solution is far from being clean and usable. Some problems:
I am not sure when and why QEvent::NonClientAreaMouseButtonRelease and QEvent::NonClientAreaMouseButtonRelease are dispatched, apart from the case considered above.
When/if one window is resized without user interaction or without the proper mouse clicks from the user, probably everything will go the infinite recursion way.
There is no guarantee that those mouse events will be dispatched the same way in the future.
Free space for more...
Proof of concept:
https://github.com/Shub77/DockedWindows

QAbstractItemDelegate painting while dragging problem

I'm overloading the paint() function in QAbstractItemDelegate (my own Item delegate class).
When dragging, it paints the contents of the entire cell, which I don't want. I'm assuming that the paint() function is called with something specific while dragging, but I don't seem to find it.
The closest I've been able to find is a QState variable in the owning view class (access function QTableView::state() is protected.) By creating a function on my QTableView-derived class called 'isDragging()' which calls that function and returns whether dragging or no, I can determine within my delegate class whether I'm dragging or not, and can modify the paint() function.
This almost works.
The problem is that it shows the modified paint image in the original cell, which I don't want - I want to leave the image in the original cell untouched.
Have to scour the examples, I guess, and see if there's something that does this...
I have crawled through the Qt source and I can see where it sets the drag pixmap by calling the QItemDelegate::paint() function, but the only thing it changes is it forces QStyle::State_Selected in the item option style. That's not enough, since the item is selected, already.
Any way to know how to draw a cell's contents explicitly when dragging?
Ok, the ultimate answer on this was to, yes, set the flag on 'startDrag', but rather than leaving it around and unsetting it on mouse release button event, simply call the base method and then unset.
The reason is that the image for the cursor is only requested (and painted) once - not continuously during the drag, as I had first thought. Leaving the flag set meant the cursor image would get drawn at inappropriate times.
So, the implementation looks like:
MyClass::dragStart(Qt::DropActions supportedActions)
{
__dragStart = true;
TableView::dragStart(supportedActions);
// request for drag cursor image happens here
__dragStart = false;
}
Why don't you do that yourself? Set a flag when dragging starts and remember the active ModelIndex, do some special painting when the flag is set, and clear the flag when dragging is finished. You can do this by overriding QAbstractItemView::startDrag.

Flex - how to create a lightbox-style loading?

My application's creationComplete="onInit()" has a lot of processing. Therefore I want to mask the entire application with a lightbox-style loading screen while the processing is being handled. The screen should be dark and a loading animation image in the center. After onInit() is completed the loading screen is removed.
My thoughts are to add a UIComponent to the stage, and to remove it at the end of onInit(). I am not sure if this is do-able or is there a better way. Thanks.
What is the initial processing you need o accomplish? Would it be more effecient to move it into initialize, preinitalize, or even a class constructor, instead of putting it creatonComplete?
That said, there is no reason you can't keep things hidden with the visible property, and swap that after your initialization routine is done. There is also no reason you can't hide items under another component until your processing is done.

Resources