Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

uilist-rewrite-wip #23

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

uilist-rewrite-wip #23

wants to merge 1 commit into from

Conversation

moxian
Copy link
Owner

@moxian moxian commented Feb 10, 2025

A branch that rewrites uilist to support text wrapping.
This is a pull request just to facilitate github comments.

I'm making no promises that I'll work on this in any near future.

Feel free to pick it up if you wish

Copy link
Owner Author

@moxian moxian left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @db48x


// TODO: why is this here
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Translation: i find it weird that hotkeys get reassigned each time the window is resized. It feels like this logic should happen elsewhere, but I haven't thought about this enough to say anything more constructive.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Heh, now that you mention it that is pretty dumb. We can move that somewhere more appropriate, I am sure.

ImVec2 available_bounds = ImGui::GetMainViewport()->Size;
available_bounds.x *= 0.9; // for aesthetic reasons
if( desired_bounds.has_value() ) {
// TODO: this should never happen actually, and is a bad API
Copy link
Owner Author

@moxian moxian Feb 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

translation: desired_bounds is used as both input parameter (i.e. things tell us where they want the window to be placed) and output parameter (things (which ones? i forgor) check this to see where we end up being placed?)
It also uses magical -1.0 to mean "i don't care, use a good default", which can not be distinguished for "please do it at -1.0 pixels". Oh and negative values also have some nontrivial meaning which i forgor again.

Basically the code around the if( !both_neg ) { line is tremendously confusing.

I think that

  • it should not be an output parameter at all (we can make a different out parameter if neccessary, but also I think we already have the window size stored and accessible somewhere) and
  • it should use std::optional instead of magic -1.0 constant

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The magic −1.0 is part of the ImGui API. std::optional didn’t exist (or wasn’t widely available) back when ImGui was first getting started. Plus, there is the small problem that std::optional takes more space than a simple float. An ImOptionalVec4 struct with four optional values would be sparse! That’s terrible for cache locality. Probably not enough to matter in practice, but we would suffer just from looking at it. I would not disagree with you if you pointed out that he could have used a NaN value instead of −1.0, but I guess he didn’t think of it.

Note that the values between 0.0 and 1.0 are also special. They don't make sense as pixel values, so we use them to represent a percentage of the viewport size instead (see cataimgui::window::draw()).

The !both_neg is rather confusing. I’ve had to think for several minutes to recall why I wrote it that way. If you look at uilist_impl::get_bounds() you’ll see that it does parent.desired_bounds.value_or( parent.calculated_bounds ). So if desired_bounds has a value, we are going to give it to ImGui when we create our window. If desired_bounds.w and desired_bounds.h are both negative then we will give them to ImGui that way. But if only one of them is negative or neither of them are then we will overwrite them with our calculated values first.

We could of course change that if there’s a better way.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plus, there is the small problem that std::optional takes more space than a simple float. An ImOptionalVec4 struct with four optional values would be sparse! That’s terrible for cache locality

I think this is all completely irrelevant because desired_bounds should never be in the hot path. It should only be read here, in calc_data (that does loads of things that are much more heavy that unwrapping an optional), and never since.

I totally agree that the actual bounds (calculated with input from desired_bounds, but stored separately) should be four floats (xywh), but that's different!

NaN

NaN comparison is even more tricky than float comparison. I strongly believe we should just use optional and not try to be clever.

If you look at uilist_impl::get_bounds() you’ll see that it does parent.desired_bounds.value_or( parent.calculated_bounds ).

It should just be parent.calculated_bounds, and desired_bounds should never be consulted by this point.
I admit I haven't thought this through any further than this, and it might not be as straightforward, but that was my train of thought at the time.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isnan is pretty easy to use, but the main point is that I am doing what ImGui is doing.

But yes, we could just always give the calculated_bounds to ImGui, having already copied whatever we needed from desired_bounds.

bool has_titlebar = !title.empty();
if( has_titlebar ) {
//
// TODO: calc_size does not account for items spacing
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other TODOs are "nice to have" but this one is the current blocker.
I failed to figure out the exact padding of the elements, and they don't quite fit (or, alternatively, reserve too much space). Changing the spacing at runtime via ImGui debug menu should demo the issue.

Small part of the problem / minor pointer is that we create a new single-cell table that holds the entire (entire!) uilist in it for some aesthetic reasons, but the way that cell padding interacts with the other cell paddings is unobvious.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The table is actually three columns, each containing a single cell. The middle column holds the menu. The left and right columns reserve space on the left and right respectively so that the caller can add their own UI in that space.

ImVec2 label_size = ImVec2(); // first column, label
ImVec2 label2_size = ImVec2(); // second column, optional (?)

// TODO: this is bad
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

translation: I have not thought about the extra_space_left / extra_space_right at all, and might be mishandling this badly. I also don't understand (read: have done zero research) why we need this at all.

desc_enabled = false; // TODO: really?
}

// TODO: what's up with header/footer horizontal padding?
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

idk, short-term debug comment probably

desc_size.x = std::max( desc_size.x, entry_desc_size.x );
}
if( desc_size.x == 0 ) {
desc_enabled = false; // TODO: really?
Copy link
Owner Author

@moxian moxian Feb 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

translation: maybe there's a better way to disable it than checking that the calculated width is 0 (it might also be fine, idk. It's a minor thing)

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair enough.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants