GTK: ScrolledWindow wants infinite space, how to limit it? - gtk

I create a large TreeView within a GTK ScrolledWindow within a hpaned within a notebook within the toplevel window (and toplevel window has a limited default size).
When I create a ScrolledWindow with TreeView within, it gets allocated as much space as TreeView takes (which is greater than the screen size) and my toplevel window is resized to infinity. How do I limit ScrolledWindow's allocation or its Viewport size? What's the mechanism that determines its default size?

Question seems to be solved. The size requisition of ScrolledWindow depends on its POLICY (in the respective direction) and is equal to requisition of child widget (treeview) if POLICY_NEVER is set, see gtkscrolledwindow.c:
scrolled window policy and size requisition handling:
gtk size requisition works as follows:
a widget upon size-request reports the width and height that it finds to be best suited to isplay its contents, including children.
the width and/or height reported from a widget upon size requisition may be overidden by the user by specifying a width and/or
height other than 0 through gtk_widget_set_usize(). a scrolled
window needs (for imlementing all three policy types) to request its
width and height based on two different rationales.
1) the user wants the scrolled window to just fit into the space
that it gets allocated for a specifc dimension.
1.1) this does not apply if the user specified a concrete value value for that specific dimension by either specifying usize for the
scrolled window or for its child. 2) the user wants the scrolled
window to take as much space up as is desired by the child for a
specifc dimension (i.e. POLICY_NEVER).
also, kinda obvious:
3) a user would certainly not have choosen a scrolled window as a
container for the child, if the resulting allocation takes up more
space than the child would have allocated without the scrolled window.
conclusions:
A) from 1) follows: the scrolled window shouldn't request more space
for a specifc dimension than is required at minimum.
B) from 1.1) follows: the requisition may be overidden by usize of
the scrolled window (done automatically) or by usize of the child
(needs to be checked).
C) from 2) follows: for POLICY_NEVER, the scrolled window simply
reports the child's dimension.
D) from 3) follows: the scrolled window child's minimum width and
minimum height under A) at least correspond to the space taken up by
its scrollbars.
So, if you set POLICY_NEVER, the ScrolledWindow will request for itself all the place, required by its child (and possibly will bloat the screen, if its child is huge). Otherwise, it will request a minimum needed to draw scrollbars. Possibly more space will be allocated for it than was requested and it will get enlarged to a reasonable size within its parent.
Sometimes you might want to have 2 ScrolledWindows, sharing a single scrollbar. Unfortunately, it is impossible to just set POLICY_NEVER for one of them and have scrolled_window1.set_adjustment() = scrolled_window2.get_adjustment() due to the fact, that POLICIES influence the size requisition and scrolled_window1 will start to request unpredictable (possibly huge) amount of space.

Related

Scaling UI element with content up to a maximum (Unity)

I have a Panel containing a TextMeshPro (TMP) object and a few other (buttons for example) aligned vertically. I want the following behavior:
Panel size expands vertically as more text is added to the TMP or more elements are added (e.g. buttons and sub-images).
As more text is added to the TMP, we stop expanding the size of the Panel and instead use overflow (ellipses or whatever).
So it should look like this when the text is long, and this when the text is short.
My current setup has Vertical Layout Group and Content Size Fitter components in the Panel object.
The Vertical Layout Group has "Control Child Size" ticked but not "Use Child Scale" or "Force Child Expand".
The Content Size Fitter has "Preferred Size" for vertical fit.
The TMP object has a Layout Element component. If I set this component to a specific preferred size it gets fixed at that size and so handles overflow well but when the text content is short there is tons of extra space.
If I uncheck preferred size from the TMP, the TMP object shrinks to fit small amounts of text snugly (as I want). However, when I add lots of text it keeps growing without a cap, eventually taking over the entire screen and more.
Is there any way to achieve my desired behavior by tweaking the settings of the Layout Element/Content Size Fitter/Vertical Layout Group? Or do I have to write a new component that turns on preferred size to cap the height when text reaches a certain length?
Thanks!
The best solution I found is to add a new component to the parent object with the Content Size Fitter that enforces the maximum.
The new component needs to implement the interface ILayoutSelfController and implement the methods SetLayoutHorizontal and SetLayoutVertical. In these methods, check if the current X and Y dimensions are larger than Serialized maxes you set in the inspector.
The full solution, from a Unity user called Democide (aka Democritus) is available here https://forum.unity.com/threads/purpose-of-uibehaviour.289666/

gtk height_for_width leading to unreasonable window heights for given width due to smaller minimum width

I am implementing a container which algins its children in a row and does kind of a linebreak when there is no horizontal space left. Thus, the required height depends on the available width. For larger widths, more content fits in one line and less lines are needed leading to less height. For smaller widths, less content fits in one line and more height is needed.
I subclassed the container and implemented the needed logic. The minimum width of the container is set to the minimum width of the widest child which would display one extreme case where there are stacked lines, some of them with only a single child inside them.
The problem is as follows: The window displaying the container has a very large height, for some cases even larger than my monitor. I am able to resize the window except that I cannot decrease the width. It turns out that the documentation for height-for width geometry management says:
Next, the toplevel will use the minimum width to query for the minimum height contextual to that width using gtk_widget_get_preferred_height_for_width()[...]. The minimum height for the minimum width is normally used to set the minimum size constraint on the toplevel (unless gtk_window_set_geometry_hints() is explicitly used instead).
Thus, the behaviour is expected as the window uses the height for the minimum width as its minimum height leading to the previously mentioned extreme case. This seems to be counterintuitive as in my case and an example used in the documentation (textflow in labels) the height will be maximal when the width is minimal vice versa. Only when actually allocating the available space, gtk considers to assign smaller heights when a larger width allows that. Even when using high widths in the window's default size and size request only the minimum width of the container is considered to derive the required height of the window.
The documentation already somehow contains a workaround, namely the geometry hints. But this seems to be a verbose and static way of sizing the window when the default width of the window together with the height-for-width-function could theoretically be used to easily determine the size of everything. The size-allocation already works as intended, only the size-negotiation cancels the benefits the height-for-width function could bring here. Is there any nice way of implementing the functionality required to fix the window sizing?
It seems as there is no intended workaround for this problem the way I searched for. The gtk size negotiation goes from bottom to top when requesting sizes and top to bottom when allocating. Thus, my container has no way of knowing how much width its parent could assign to it.
I solved the problem by adding a property which defines the minimum of children per row. This can be used to increase the minimum width and therefore decrease the minimum height. I only use it for the minimum width calculation while actually ignoring it doing the real size allocation which only is a minor detail I will document.
This documentation will be part of the code example I will provide as an answer to my old post which was about implementing a FlowBox with the behaviour described above.

Unity list item is leaving extra space in high resolution

I have a vertical Scroll List. I have developed this app for resolution 768*1024. In this resolution my List is working fine. But when I run my app in higher resolution(1440*2960) it leave some space around all 4 direction.
I have also tried with changing Layout element min height dynamically, but Spacing issue is still exist.
Vertical and horizontal layout set element position in (screen width/height divided by a number of elements) * element number, in other words, they space out all elements evenly across canvas space. To achieve what you want you either have to enable child control size -> height option or write a script that aligns your elements in the center of the screen and one after another taking in consideration their height.

Make intrinsicContentSize adapt to external constraints

The Context
I often have situations where I want multiple NSTextViews in a single NSStackView. Naturally, Auto Layout is not pleased with this since this makes height ambiguous (assuming the stack view is not set to fill equally). Even after adding constraints to resolve these issues, macOS Interface Builder appears to have a bug where it refuses to actually update frames when asked.
For this reason and others, I'm attempting to create a TextBox class (subclassing NSView) to encapsulate an NSTextView (and associated scroll view) and include an intrinsic content size to avoid layout issues. The intrinsic content size would be calculated based on a user-specified min and max number of lines (to display without requiring scroll). In other words, up to a certain max number of lines, TextBox will resize itself so that scrolling is unnecessary.
The Problem
This would seem to require an intrinsicContentSize that is dependant on frame width.
But, intrinsicContentSize documentation states:
The intrinsic size you supply must be independent of the content frame, because there’s no way to dynamically communicate a changed width to the layout system based on a changed height.
However, Auto Layout Guide states:
A text view’s intrinsic content size varies depending on the content, on whether or not it has scrolling enabled, and on the other constraints applied to the view. For example, with scrolling enabled, the view does not have an intrinsic content size. With scrolling disabled, by default the view’s intrinsic content size is calculated based on the size of the text without any line wrapping. For example, if there are no returns in the text, it calculates the height and width needed to layout the content as a single line of text. If you add constraints to specify the view’s width, the intrinsic content size defines the height required to display the text given its width.
Given that when scrolling is disabled in a text view:
If you add constraints to specify the view’s width, the intrinsic content size defines the height required to display the text given its width.
Then it seems there is a way to do what I want by perhaps looking at existing constraints.
The Question
How can I define an intrinsic content size that calculates height based on otherwise specified width, as described in the last quoted sentence above?
The solution should produce the effect described in "The Context" and not produce errors or warnings when used inside a Stack View.

Width limitations with constraints when window resized

I am trying to add a custom view in an nswindow in my osx app.
I need to give a minimum and maximum width values for the custom view which is located in the centre. The view's width should expand until a certain point (maximum width value) but should stop expanding if user continues to expand the window.
Thanks in advance.
You can do all of this with layout constraints.
First, we need to specify where the view should be relative to the window. For the sake of this tutorial, I'm going to assume you want it centered:
Next, we add our constraint for the minimum width:
To make this a minimum instead of an absolute width, click on the constraint and change it to "Greater Than or Equal" in the Attributes Inspector:
Now do the same thing, making another width constraint for the maximum. This time, set it to "Less Than or Equal":
Now the width constraints are set up. But we're not done. We've now set a minimum and a maximum, but the width is still ambiguous—there is no way for the layout constraint system to decide what exact width between 300 and 700 that it should actually be using at any given point. There are two steps to fix this. First, we need to make sure that the view will be entirely within the window and not run off the edges, so create some Greater Than or Equal constraints making sure it stays within its bounds:
(Also, make a trailing constraint which is set up identically).
Finally, we need one last set of constraints; we want some leading and trailing constraints, marked Equal, but with a lower priority:
(Also add a trailing constraint, identically configured)
What does this one do? Well, it tells us that, unless our other constraints (specifically the maximum width, in our case) make it impossible, we'd like the edges of the view to be the standard distance from the window edge. The reason we use 499 as the priority is because that the value of NSLayoutConstraint.Priority.windowSizeStayPut is 500. The documentation has this to say about .windowSizeStayPut:
It's generally not appropriate to make a constraint at exactly this priority. You want to be higher or lower. Constraints with higher priorities can adjust the window’s size. Constraints with lower priorities must be fulfilled using the current window size.
If we set our constraint to higher than 500, the system would restrict us from making the window too wide for these constraints to be valid. That's not what we want, since we want the edge spacing to expand in this case. So since we want to be able to break this constraint by resizing the window, we set it to slightly less than 500—so, 499. This means that the constraint system will try to put the view here, but if it can't do it because we made the window too wide, it will allow this constraint to break, although it'll still try to get as close as it can without breaking the other constraints. So your view will be at its maximum width, and centered in the window.
Voilà!