Is creating a journal/notebook with 2 pages and random text lengh actually impossible?

Hey,
I know everything is possible, but this one looks pretty simple, and yet, I just cannot do it.

Basically, you have this kind of set up that represents two pages (W_journalPageLeft/Right is just a widget that contains a single Vertical Box), and then you have an array of 10 texts:

You loop on those texts, and, for each, create a W_entry which is added to the left page.
If the current W_entry would go out of bounds, then add it on the right page instead.

It should be so simple. The algorithm is easy. But getting the correct value is a nightmare. I had so many troubles, like when/how to use delayUntilNextFrame, or ForceLayoutPrepass, or a mixed of both, get desiredSize, or GetCachedGeometry->GetLocalSize?
I even had some crazy stuff like, when adding delayUntilNextFrame inside the loop, some Bp were just skipped.
And later on, adding a breakpoint and going through each node would run the code. If no breakpoint, absolutely nothing would happen (probably because going trough a breakpoint will go the the next frame so the UI will be created, but still crazy to think about it).

Any idea about this? I am so lost over such a basic stuff.

Yes this is kind of basic, can you share your screen shots of bp graphs or code? That way would be easier to guess and help in return for many community members.

So from what you said. This is 2 Container Widgets

  • JournalWrapper
    • LeftPageContainer->VerticalBox
    • RightPageContainerr->VerticalBox

and a JournalEntry_WGT so basically its a templating job. Not sure how you handle entry data but just to demonstrate the loop.

FOR Index = 0 TO 20

EntryWidget = Create Journal Entry Widget

IF Index >= 10
    TargetContainer = Right Page Container
ELSE
    TargetContainer = Left Page Container

Add EntryWidget to TargetContainer

END FOR

Thats it there shouldn’t be any delay, break etc needed. You can do like that if you have some specific needs like very large data so you want to make array operations but its not necessary at this stage. Also as a side not you can not delay loop, there is ways to delay loops however by design loops are not delayed in unreal blueprints.

You can do something like this

Made a Page this is basically a page with verticalbox

Our entry widget JournalEntry_WGT

For our journal made a canvas, added a horizontal box, inside dragged our recently created pagecontainer_wgt 2x and renamed as left right

For logic simply we can do for now in design time, you can do on construct but would work like this aswell.

result

You can now put journal wrapper anywhere. like hud or menu and when construct calls it genrates it

Just for to demonstrate data, you can have a string array for now or maybe you have stuct or data table.

So over here first starts loop for the array length. then if greater than 20 breaks so pages never can overflow after 20.
then check if this is less then 10 or greater than 10, if less select left and greater select right.

Happy developing.

It might be easiest to use a fixed width font and count your characters (Length) in the input string and use that to make your wrap decision. Otherwise you’re into the issue you discovered with having to pump a render frame to get slate to return the text height.

You could keep things Visibility Hidden for a frame (maybe within a Hidden overlay), populate all at once, step a frame then measure and reparent them to their respective page widget, setting them Visible at that point.

Using c++, you can use the FontMeasureService to get all kind of data about fonts and rendering, but I wouldn’t go this route unless you’re comfortable with slate/umg c++.

Hey Grimnir,

Thanks a lot for such a detailed answer! I should use your way of doing branch + select, more elegant than mine you’ll see!

But this is not exaclty what I am trying to do. Rather than setting an arbitrary number of entries per page, I want to be able to determine it dynamically based on the total height of all those entries. Let me post my code with all the comments:

At the end, the resut looks like this:

Logs:

LogBlueprintUserMessages: [wJournalTEST] Start of the script, the height of the page is:650.0
LogBlueprintUserMessages: [wJournalTEST] remaining size: 573.0
LogBlueprintUserMessages: [wJournalTEST] remaining size: 496.0
LogBlueprintUserMessages: [wJournalTEST] remaining size: 419.0
LogBlueprintUserMessages: [wJournalTEST] remaining size: 342.0

So it looks like something is off with the height of the journalEntries. 350px is more than the half of the left page, and yet we can see that the 4th entry is still printed on the left page (I am pretty sure, visually, than we are below the 350px mark. Yet for the UI, we were at 419 (342 being printed at the very end, once the last entry has been processed).

My journal Entries:

1 - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi blandit id risus vel scelerisque. Sed facilisis et dui et feugiat. Morbi metus sem, porttitor sit amet dignissim ac, facilisis vel velit.

2 - Etiam at enim sapien. Curabitur ligula magna, venenatis vel volutpat sit amet, posuere vel lectus. Donec condimentum suscipit turpis, a faucibus erat luctus vitae. Duis gravida ornare sem, non fermentum ipsum maximus sit amet.

3 - Ut id lacus ligula. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Morbi eu magna non purus vehicula hendrerit. Morbi euismod eu dolor at euismod. Donec euismod rutrum dolor, a accumsan sapien feugiat id. Morbi finibus lectus sit amet feugiat hendrerit. Aenean vulputate blandit laoreet.

4 - Maecenas porttitor consectetur sollicitudin. Etiam venenatis vehicula diam. In ut facilisis orci, a consectetur turpis. Quisque consectetur risus sed maximus dignissim. Morbi eget nulla libero. Donec tempus fringilla magna, non varius ligula. Morbi eu nunc at velit rutrum pharetra quis at ipsum.

I understand that it actually comes down to what actually @Countsie mentioned after my reply to your post. Which is valid and can be done in that way, I will go a bit oldschool logic and show you that. Also think its good to mention that this is for vertical box entry templating, If I am not mistaken some of the stuff already done in ListView widgets so you can use that if you prefer.

Are you using slate to return widget heights of entry or something else like get desired size. I think you are using get desired size since you mentioned delays etc. which makes sense, since it can be only accurate after entry added and rendered.

For immidiate results you should have a hook function to slate or we can delay in blueprints wait for an accurate geometry then do calculation.

RootWidget->ForceLayoutPrepass();

float Height = EntryWidget->GetDesiredSize().Y;

or from draw

float Height =
    EntryWidget->GetCachedGeometry().GetLocalSize().Y;

So you know the height accurately before deciding adding another one or not would require a delay from draw in this case, we could have delayframe/delayuntilnexttick combos however there is a free plugin which has some nice delay functions and many other nicer things will use that.

I would do to that and getting the entry widget cached size .y first, then subtract from container and see if the value is still >0 then add it. Even can have a safe zone like 32 etc.

if RemainingSpace > 32 ->DoEntryAddition., check page etc.

Here is the logic with explanations

Simply, get a page, evaluate entry, accept or reject continue - > limit reached look for new page → continue

here is the functions

If you hook the draw journal to an even from controller or preferably from C++

UGameViewportClient::OnViewportResized

Would work nicely / acceptable visual quality. As mentioned better do his with listview or you can do much better from C++ with a hook on slate level immidiate size gathering so there is no delay visibly. In this example I hided this delay with a simply entry fade in animation, delay is 2 3 frames still is a delay and somewhat a walkaround the problem in a smart way. We can also take approach to create all widgets then decide what fits in order and not (batch process) that would be a bit better however for demo think this is good enough but ofcourse : Let us know.

I just saw you edit and your graph. As said this is a bit about handling the widgets and their sizes issue.

1- Loop : Which you are and I am doing at post #4. After adding a widget to a parent, the geometry not settled yet and the size highly likely to be incorrect even if you do forceprepass. After adding you should wait 2-3 frames delay to settle and get a correct sizing and then would be correct. Even this is a hacky way of doing it. Think we can do from C++ better since we can do event driven over there, wait for invalidation and validation of widget then measure after a guaranteed layout phase size etc.

2- Batch : Create widgets first, render but dont show (opacity 0), evaluate sizes, make batch insertion. This would be the nicer/responsive way of doing it. Centralize pagination in one pass so jittering is not visible at all.

If you have C++ I can give example, if I am not mistaken did this in an options menu in a similar way.

Hey again @RedGrimnir

I know pretty much nothing about C++, even if, I swear, after years of BP I tried it (took a class from Udemy) but, yeah, unfortunately it did not click at all for me. I find it sooo tedious compared to BP. :frowning:

So I know nothing about the FLslateFontMeasure Countsie was talking about or your lines of codes (although I think I understand them, I just don’t know what to do with them).

But then I took at look at your BP. And again, this is a clever way of looping without doing a forEach, especially since those forEach seem to hate any kind of delay! Same for functions!

I tried it, and it worked pretty well. Thank you very much for taking the time to show all your functions/BP, now I think I have a solid fundation I could build upon. For instance, perhaps create the entry, then take its height, do the currentHeight - entryHeight, and if < 0, it means the new entry is out of bond so we can remove from parent (rejectEntry) and add it the the nextPage.
I need to think about it.

And now that I’ve read again @Countsie answer, especially the second line, the batch stuff, I get it now, and this is something I have to try!

Thanks again to the both of you!

My pleasure, no worries think its fairly doable in bp. C++ has some hooks that doesn’t exist in BP so we have to workaround but with some C++ anything is doable for sure.

Batch can be something like

For the batch not a big difference but something like this should work.

Functions

Also you can evaluate viewport size changes like this.

Evaluate On tick and call custom event

Draw Journal Again

Should behave similar to this.

Happy developing.

Hello,

Thanks to your help, I am getting there.

I have two more questions, if you don’t mind.

First, I’m not used to working with delays, and I’ve noticed something annoying: when I set a breakpoint to debug my code, every time execution goes through a delay (or a “latent node”), it stops stepping through node by node and just continues execution. Is there a way to keep stepping through each node?

Second, a more general question: I often have a main widget with many nested child widgets, for example:
widgetRoot → widget1 → widget2 → widget3 → widget4

All the logic runs in widgetRoot, so all children need a reference to it. To achieve that, I usually pass the widgetRoot reference from widgetRoot to widget1, then widget1 passes it to widget2, and so on. This can go quite deep in the hierarchy, and I find it a bit ugly. Is there a better way to handle this?

Unfortunately not. Latent nodes suspend Blueprint execution, so the debugger usually won’t keep stepping node after a Delay. What you can do is to put another break point after the Delay, or add temporary Print Strings around the delayed section to see the execution order.

For your second question about UMG referencing and communication (chaining), yes, there are multiple ways to do it.

For BP:

1- You can always get the parent owning widget, and if you know the parent you can always cast to it. It’s “OK” to hard-reference like that. You can always walk up or down in UMG if you know the hierarchy. You can also use GetOuter().

2- C++: I know you are not into it, but this can be automated. It can be a subsystem, custom inherited user widgets that have an instigator, or even a struct that refers to player, HUD, owner, etc. by default. You can also do this manually in BP. For example, when creating a widget, always provide information about the instigator widget/object.

EX: You have an on-screen quest marker attached to an actor.

HUD (Parent) → IndicatorContainer (Parent) → Indicator (Quest)

IndicatorContainer → SpawnIndicator(Quest, ThisActor)

IndicatorContainer can now forget about the child. The child should do its own job.

The quest indicator checks range, has its own functions, moves around, displays, blinks, and when conditions are not met it goes away. Parent doesn’t care about the child, child doesn’t care about the parent. As you mentioned this can become a frustrating job if too much nested/chained hierarchy you have.

3- Event-driven design. This is generally what I use. It’s clean and extendable to huge systems. Parent listens to events, child broadcasts. Child never knows what the parent is, child always does the given job.

EX: You have a horizontal menu. The menu has N number of buttons. Parent creates buttons and binds to their clicked events. Buttons only dispatch:

OnClicked(Object)

Parent knows what was clicked.

4- Shared References (advanced): You load this on a controller or somewhere else and keep references in a shared place. You know which menus are created and can access them from the root.

Subsystems in C++ are very nice for this. You can have layers of information, track which layer holds which widgets, what is shown or hidden, and access them anywhere at any time.

5- MVVM: Also valid, especially if your UI is more data/state driven. Instead of widgets directly talking to each other, widgets bind to a ViewModel. The ViewModel holds the state, and widgets just read/update that state.

This is clean for things like inventory, stats, quest logs, settings menus, etc. because multiple widgets can react to the same data without needing direct references to each other.

However, I wouldn’t force MVVM for every small widget interaction. For simple child-to-parent communication, event dispatchers are still faster and simpler and nicer.

Big update because I managed to solve all of this without the use of a delay (pretty much). This might help some people.
At the end of the day, I only used a delay in the construct event:

set visibility to visibile but opacity to 0 → delay 0.2sec → set visibility to collapsed and set back opacity to 1.

The reason is, now you don’t need the user to open the journal a first time to get the cached geometry/desired size if you want to do stuff before the user open the journal (like loading a save for instance)

I realized the use of delay could be prevented. If you don’t use auto wrap box, then force layout process can work as long as your text will be written on a single line. Then you can calculate the height of each widget, etc..
But if you have long text and need the auto wrap, then you’re screwed because it looks like it will first create the widget (and you’ll get the height) but then it will recalculate the lengh of the text and go to the next line if needed, which will increase the height of the widget, and this new height will be different than the one you got. To get it right, you would need to wait for a few frames, so the use of the delay node.

BUT you can use “set wrap at”, and if you do that, the value will be pre determined so there is no need for another calculation. The widget seems to already know where to go to the next line if you text is too long, so the height you’ll get from getDesiredSize is correct.
And even better, since we cached the geometry of the journal in the event construct, you can get the width of your journalPage (from getCachedGeometry->getLocalSize), in the journalEntry, set a variable exposed on spawn and instance editable (a float) and feed them with the width of the journalPage upon creating that journalEntry.
And in the journalEntry, in the construct event, get your “content” text widget, and setWrapTextAt with that float value.

So basically, what I’ve done is:
Get all the stuff you need to add to the journal
Get the height of an empty page.
Fill that page at once with all the entries (create widget) you want to add.
Once this is done, I parsed all the newly created entry widget, got their height and add them to “currentHeighTaken”. Each time, I compare that to the pageHeight and do some stuff if > or < to pageHeight, like adding this entry to an array of structure.

And at the very end, I have an array of structure composed of entries (date + content), each index is a page of my journal.
So for instance, when I want to open the journal to the page 3, I get the index 2 of my journal and print on the left page the all the entries from index 2, I check if index 3 is valid, and if so, I get its content and print it on the right page.