How to get notified when changes in the document actually happens? - javafx

The following question is about RichTexFX. For standard JavaFX components, I've never had this issue before.
In my application, I am adding text content to the end of a GenericStyledArea (actually a subclass of it, but that should have no impact on the behaviour I am observing). Every time new content has been added to the end of the document, I scroll to the bottom so the new content is always in view. I use the following code to scroll to bottom (actually written in Kotlin, but I have translated it to Java below):
showParagraphAtTop(document.getParagraphs().size() - 1)
This almost always works. However, in some cases the screen will not show the newly added element. I have come to the conclusion that by the time showParagraphAtTop is called, some internal state in richtextfx has not been updated. This is because I added a button that I can click on to call the code above, and if I click it after the scroll failed, it scrolls to the bottom as expected.
I have tried various ways to work around this, but the only solution that works reliably is to do the following:
FxTimer.runLater(Duration.ofMillis(10), () -> showParagraphAtTop(getDocument().getParagraphs().size() - 1));
In other words, I force a short delay before actually scrolling the screen.
Even though the above works, it's incredibly unsatisfactory, and there should be some way to do this the right way. However, I have been unable to figure it out.
I saw another question (Autoscroll JavaFX TextFlow) which suggested the use of layout() prior to scrolling. This does not remove the problem, although it does appear to make it less common.

Related

JavaFX ComboBox border is wrong if the drop-down drops-"up"

I am using "javafx.scene.control.ComboBox" on Java 8 and I noticed that whenever the combobox does not have room below and instead pops up, the bordering styling of the elements switches as if it still pops down.
How can I access the styling for that to fix it?
Managed to fix this by actually extending the ComboBoxListViewSkin. In there, I've stuck a method that updates the styling, and does that by calling super.getPopup(), gets the AnchorY of that and compares it with the combo-box Y. After determining if the popup is below or above the combo, I set the correct styling on super.getListView...
Also, that method I've added, has to be called from the "ON_SHOWN" event of the combo-box.
I've tried several other variants but the damn thing just yields unstable behavior.

NSTableView detect row scrolled off

I have a view-based NSTableView and each row has to generate some images which takes time. These are done on a background NSoperationQueue and started when a request for an image is made (through a binding).
When a row is scrolled off screen, if the NSOperation is still pending, I'd like to be able to cancel it, but I need to detect when a row is no longer visible. Should I be looking for the delloc on my CustomTableRowView, CustomTableCellView, or something else?
I guess I need the opposite of tableView:willDisplayCell:forTableColumn:row:
Some sort of tableView:willHideCell:forTableColumn:row:
Apple's TableView Playground has a comment:
// We would have to keep track of the block with an NSBlockOperation, if we wanted to later support cancelling operations that have scrolled offscreen and are no longer needed. That will be left as an exercise to the user.
I can cancel the operation, but I am not sure how to detect when a row is scrolled off.
I believe this is solved with
tableView:didRemoveRowView:forRow:
though I am not sure if it is called immediately after the row scrolls off or a short while later.

QMdiArea: First tab works fine, second tab's content is way too small

Situation: As the central widget of my application I have a QMdiArea in tabbed mode (wrapped in a custom class that inherited from QMdiArea). When I now add the first QMdiSubWindow via addSubWindow() everything is still fine, meaning the window and its contents are maximized to take up all the space of the QMdiArea. However, as soon as I add a second sub window, the following problem arises:
Problem: The content of the second window is not displayed, but instead the content from the first continues to (despite the fact that the second tab visually has the focus), but is reduced in size. It only takes up a few pixels. But if I now switch back to the first sub window/tab (either by clicking into the size-reduced widget which then automatically goes back to max size or by selecting the first tab header) and then selecting the 2nd again, all is fine = the widget/editor of the 2nd tab is now displayed and is maximized.
But only until I close the second tab, in which case the content from the first is displayed (as expected), but again only in the small size!
Screenshot:
Another related test case: 1st tab is created and is displayed full size as expected. If I now simply resize the overall QMainWindow, suddenly a faint border appears around the widget in my tab. So it appears to be in a mixed state between maximized and normal mode with borders. Does that create any new leads?
Screenshot:
I am so far not using any explicitly defined layouts, but given that the first tab works fine and after manual back-and-forth switching all others as well, I assume that it should work without.
Do I need to set explicit layout objects? Why does it work for the first tab, but not the second?
Some code as a base: This is the logic executed in my custom QMdiArea class that takes care of adding the new children:
// Note: pEditor inherits from / is a QMdiSubWindow itself
if(!pEditor->isInitialized())
{
pEditor->initialize();
pEditor->setWidget(pEditor->getEditorWidget());
pEditor->setInitialized(true);
}
pEditor->beforeDisplay();
addSubWindow(pEditor);
pEditor->showMaximized();
// HACK START
pExisting = subWindowList().at(0);
if(pExisting)
setActiveSubWindow(pExisting);
// HACK END
Update: Added the hack proposed by N1ghtLight. Marked as such in the coding.
Update 2: Edited / correct problem description + new test-case with screenshot.
After many hours of comparing the example project from N1ightLight to my own implementation, I finally came across the central differences. There were in fact two issues at play:
For some reason I had the following line in the creation of my MDI sub windows, which apparently screwed up the size handling. Getting rid of this line removed any need for the previously proposed hack. Combined with the layouting proposed by N1ghtLight, all size changes are now handled gracefully.
setWindowState(Qt::WindowMaximized); // do not use that state!
The second part regarding the tab closing (closing one tab did not bring up the next tab in the expected manner and size), is actually explained in the Qt documentation:
When you create your own subwindow, you must set the Qt::WA_DeleteOnClose widget attribute if you want the window to be deleted when closed in the MDI area. If not, the window will be hidden and the MDI area will not activate the next subwindow.
Since I was creating my own sub windows I had to set that flag, but I never did because I wanted to prevent Qt from automatically deleting my content widget as well. My solution now is that I set the flag, but whenever a MDI child is about to be closed, I remove the link to the widget, so Qt cannot delete it.
void CustomMDIWindow::closeEvent(QCloseEvent* pEvent)
{
setWidget(0);
pEvent->accept();
}
I will mark my own answer as the correct one, but will award the bounty to N1ightLight since his support eventually led to me finding the final solution.
The ugly workaround really can be the calling of setActiveSubWindow() twice: First for some available sub window and then for the newly created one. This should emulate the situation when you click on the first tab and then return to the second one.

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

Flex Post Event Screen Updates

I came across this topic today while investigating something very strange. Doing certain things in our Flex app can cause the number of frames rendered to rocket, from 12fps to ~30fps: loaded animations start playing at high speed and the GUI starts to lock up.
Since everything I've read on Flex/Flash hammers home the point "the frame rate is capped at the fps set in the top level app", it seems the only way these extra renders can be happening is due to some events causing them (no programmatic changes to the stage's framerate are done anywhere). Since it only happens when I put my update logic in the ENTER_FRAME handler, I'm trying to figure out what might be happening which to apparently causing Flex to go render-crazy.
Hypothesis: something in my update function is triggering an immediate screen update, this raises another ENTER_FRAME immediately, which means my update loop gets called, which triggers another immediate screen update, ...
We have Flex components used in our GUI, if this is a factor. I don't really know where to go next on this.
Clarifications:
When I say things speed up, there
are two ways this manifests.
Firstly, my ENTER_FRAME handler gets
called far more often.
Secondly, a
loaded Flash SWF with a looping
animation built in suddenly speeds
up to te point it looks silly.
I am not using updateAfterEvent, I only
found this existed when researching
this problem. Apparently, some
events on Sprite subclasses
automatically call this and I wonder
if that's the root cause.
I am not doing any direct messsing about with rendering at all. Background animations play automatically as they have timelines built-in from CS3 authoring, all our update function does is change the position of DisPlayObjects or add/remove them etc
Update:
I added a label to my app to print out stage.frameRate, and discovered at certain times, it suddenly changes from 12 to 1000 (the maximum allowed value). While it was trivial to add a line to my ENTER_FRAME handler to reset it that's hardly a big help.
Also, even doing this, the rendering is all messed up. Certain actions (like raising an Alert popup) make it all spring back into life.
Unfortunately, I am not able to view the source of the Stage class to set a breakpoint on the setter property.
That's very interesting about the Flex loading 'set to 1000fps' thing. What we have are several Flex applications which all provide a common interface. A master app is in charge of loading these modules as required through the SWFLoader class. However, the loading process already takes into account the delayed loading... when the SWF loads we then wait for the APPLICATION_COMPLETE from the SystemManager. Once this is received, shouldn't the applications completion have occurred?
Flex sets the frame rate to 1000 during "phased instantiation" of Flex components, which occurs only during initial load of a flex swf. This allows it to build all components very quickly.
Are you waiting for the Flex app to be fully loaded and constructed? You should be waiting for FlexEvent.CREATION_COMPLETE before working with your Flex content.
If you would like a reference to where this occcurs, look in the Flex LayoutManager class, line 326 (using Flex SDK 3.0.194161), in the setter for the property usePhasedInstantiation.
Update:
APPLICATION_COMPLETE should have you covered for the initial load.
This actually happens any time components are created directly from MXML. So there are a few other cases to look for. Are you using any Repeaters? Do you use any navigation containers that are building their children on demand?
One thing I'm not clear on - are you seeing that the actual screen refreshes are occurring faster than the published framerate? Or is it that your animations are moving faster but the screen refreshes are unchanged? (i.e., it used to move 10 pixels per second, but now it moves faster than that, regardless of how often the screen is drawn.)
An easy way to check this would be to try publishing your content at 1 fps. It should be clear whether the screen is redrawing once per second, but animated elements are being moved more frequently than that, or whether the screen is actually updating more frequently.
If the latter, are you using any updateAfterEvent() methods in your code? This can cause actual screen refreshes to occur faster than the published framerate. It shouldn't affect ENTER_FRAME events though. You should still only get one of those per frame update.
Alternately, are the things you're animating just Sprites and so on, or are you implementing them as Flex components, and trying to redraw them with invalidate() methods and RENDER events and so on?
If you could clarify a few of these points in the question the answer might be clearer. Thanks...
Thanks for the clarifications. If a loaded clip with a animation (I assume you mean a frame animation) is speeding up, then that certainly sounds like something is changing your playback framerate, as opposed to other things that could be going on. With that said it's not a problem I've seen crop up before, but I do think there are some things you could try that ought to narrow down where the problem is:
You might as well try tracing out stage.frameRate during the speed-up. Presumably nothing ought to be changing your framerate, but since that would explain your issues you might as well rule it out.
Try removing as many GUI components as possible and seeing if the problem still occurs, if it's possible to trigger the problem without them.
One sanity check you could try, if it's feasible, is to copy some of the contents of your game into a fresh project and try it there. Sometimes mysterious issues like this happen because some class or SWC is being imported somewhere that everyone forgot about.
You could try driving your code from a different event. For example, as far as I know driving it from Event.EXIT_FRAME or Event.FRAME_CONSTRUCTED ought to look the same, but if it doesn't then that's a hint. Alternately, you could try driving it from something like a keyboard event or MouseEvent.MOUSE_MOVE. Then if updates occur even though you're not firing events, you'll know something else is driving things besides your event loop.
Those are the things I'd try, anyway. Hope you track it down...

Resources