GNOME Bugzilla – Bug 560931
Add PANGO_WRAP_NONE
Last modified: 2018-05-22 12:44:08 UTC
In a HippoCanvas type of layout system (presumably similar to a GTK extended layout), the size request consists of two numbers, minimum and natural request. If ellipsization is enabled, the minimum request should be "a few characters" or 1 or some other small number; the natural request should be the most space that can be used without introducing padding, i.e. the exact request that shows the entire text but nothing else. So for an ellipsized-horizontally label, what you do is measure the layout with width -1, to get the natural width, in size request; then in allocate, set the width to the allocation to force the actual allocated width. Vertically, though, the problem is that the wrap-first-then-ellipsize-second behavior cannot be enabled *except by setting the height*. What's missing is a way to provide a fixed width to height request, then see what the height would be to show all the text at that width. In other words, if no height is set, a PangoLayout that is going to have the wrap-first-then-ellipsize behavior should return the wrapped height, not 1 line of height. I believe a workaround for now would be, in the width size request, to disable wrapping and enable ellipsization; then in the height size request, enable line wrapping and disable ellipsization; then in allocate, set a height and re-enable ellipsization. But this is going to be pretty bizarre code in GtkLabel. Width request/allocation doesn't require these hoops. Potentially, the root problem is that pango_layout_set_height() effectively changes the wrap setting, and the wrap setting doesn't do anything for ellipsized labels. It might work more cleanly if enabling wrap *and* ellipsization were not pointless as it is now (right now, if ellipsization is enabled it seems to "win" and the text never goes to multiple lines, unless a height is set). Instead, enabling wrap could always mean "wrap first, then ellipsize" and setting height would be required to ever actually get any ellipsization with wrap enabled. Setting height with wrap disabled would perhaps do nothing. With this change, to get the wrap-then-ellipsize behavior, you could leave both wrapping and ellipsization enabled in all of width request, height request, and allocate. In other words it would work just like the width case. Long story short I'm wondering if wrap-then-ellipsize behavior should be requested by setting both wrap and ellipsize, while right now it's requested by setting the height. But, I guess there are ABI problems here. And I'm probably missing something.
<hp> owen, am I missing something in http://bugzilla.gnome.org/show_bug.cgi?id=560931 <owen> I'm a little confused. Don't you have to pick a fixed width for an idea of "natural height" to be meaningful? <hp> owen, yes, but the height request method has a for_width the basic issue (I guess I explained it at too much length) is that if you size the layout to width=for_width, height=-1, then the layout's height is always 1 line <owen> hp: You turn off ellipsization,right? <owen> With ellipsization on, the 1 line height sounds exactly right <hp> I think if wrap=true it isn't <hp> setting height makes it wrap, even though wrap=false ellipsization=true I think setting height shouldn't change wrapping <owen> hp: That sounds like a bug, but why does that matter here? hp: Don't you want line wrapping? <hp> to get wrap-then-ellipsize you should have to set wrap=true ellipsize=true <hp> so I want wrap-then-ellipsize I believe this means in height request, you have to set wrap=true, ellipsize=false and in allocate, you have to set wrap=whatever, ellipsize=true this is going to require a huge explanatory comment in ClutterLabel <owen> wrap=false, ellipsize=true, height=set is a weird case ...do you ellipsize each line, then if there are two many of them add a line that is just ...? <owen> hp: Either you or me is thinking about this wrong, since I see nothing confusing at all abou tit <hp> wrap=whatever, ellipsize=true, height=set is how you get wrap-then-ellipsize-last-line behavior <owen> You leave wrapping on, since you always want wrapping. When you want to compute the natural height request you termorarily turn ellipsization off because you want to find the non-ellipsized height. When you actually go to draw, you set the allocated height. <hp> OK, but, if ellipsize=true and width=-1, then pango_layout_get_width() returns the full length of the text so how come for height, enabling ellipsization means I won't get the full height of the text? <owen> But ellipsize=true, height=-1 already has a meaning ... which is "ellipsize each line" Including the 1 line case <hp> by line do you mean newlines in the text itself, or wrapped lines? <owen> hp: I guess the question is whether it would have been OK for behdad to change the behavior of having both ellipsization and wrapping on with the height set <owen> err, with the height *unset* <hp> yeah, I mean there's an ABI question, but first I wanted to understand what was sort of right / made sense <owen> hp: The existing behavior was that turning on ellipsization meant that wrpaping never happened <hp> unless you set a height, in which case wrapping is magically enabled even if it isn't <owen> hp: If that was changed so that if wrapping was on ellipsization never happened, then what you want would be possible. <hp> that's what I think is logical, is that if wrapping is enabled we always first wrap, then only ellipsize if there's insufficient height <owen> hp: Well, I guess setting the height puts it out of "compat" mode. hp: Yeah, that would make more sense to me to, assuming that the you made the "compatible enough" judgement call. <owen> But if you don't convince behdad to change anything, I'd strongly advise against putting a long comment into ClutterLabel , because your long comment in bugzilla just confused me more :-). Just switch to turning off ellipsization for the width case as well since I think the only thing that is confusing is that the -1 technique works for width and not for height <hp> I think I also have to set wrap=true in height request, even if the ClutterLabel has wrap=false, because when setting the height in the allocation will implicitly enable wrapping; or I have to not set the height in allocate if ClutterLabel::wrap=false either of those will be kind of inexplicable if reading ClutterLabel code <owen> hp: Well, the latter is much less confusing, since the former would make the 'wrap' setting meaningless hp: And could be explained with simply "Vertical ellipsization is only implemented in Pango for line-wrapped paragraphs" <hp> the wrap setting would still change the width request, in the former case <owen> hp: So you are saying "wrap=false" would mean "best effort don't wrap" ? <hp> owen: I guess; well, that's what wrap=false means in PangoLayout, basically, if you set the height, and I'd just be passing it through <owen> hp: I think there needs to be a way of saying "keep this single line of text a single line" <hp> so ClutterLabel would make wrap=false mean "never ever wrap", by not setting the height with a comment "/* if we set the height, pango would force wrapping on, so we can't set the height */" I think if PangoLayout has wrap=false it should never wrap (regardless of height setting) and if it has wrap=true it should always wrap (even if height is -1) <owen> hp: I have a feeling that things will just get weird otherwise with height-for-width hp: it's clearly wrong that setting a height turns on wrapping hp: a Pango bug hp: that's a separate issue from whether with height unset and ellipsize=true,wrap=true whether it should wrap or ellipsize <owen> hp: And also separate from whether there's anything meaningful to do other than overlfow when ellipsize=true,wrap=false,height=set,natural-height > height. <hp> owen: the reason the two issues are somewhat connected is that right now, because ellipsize=true wrap=true doesn't work (it just ellipsizes, never wraps), the only way to get wrap-then-ellipsize is to set the height, which enables wrapping even though we're ellipsizing <owen> hp: there is no difference between "wrap" and "wrap-then-ellipsize" if the height is not set, right? <hp> while if ellipsize=true wrap=true worked, then the setting-height-turns-on-wrapping bug would not be useful (if you fixed the bug right now, then there'd be no way to wrap-then-ellipsize, I think...) <hp> owen: no difference, no
To sum up, I guess the two issues are: * wrap=true, ellipsize=true does not do any wrapping if height=-1 * wrap=false, ellipsize=true does wrap if height is set And therefore if a widget has a "wrap" setting, "ellipsize" setting, and a size allocation, it can't just pass those through to PangoLayout. * giving pango the size allocation with pango_layout_set_height() may override wrap=false on the widget, so widget must special-case not to set height if wrap=false * giving pango the wrap=true setting inside height request (with height of -1) will not work if ellipsization is set, so widget must not set ellipsization in height request even if the widget setting is ellipsize=true; the widget then has to turn pango's ellipsization on in size allocate. This handling isn't required right now for width request and width allocate. It would be simpler if width and height request were always "set the pango layout to match the widget settings, then get layout width/height", and if allocate were always "set the pango layout width and height" This is the case right now for width but not height.
http://bugzilla.openedhand.com/show_bug.cgi?id=1266 has the ClutterLabel patch to use pango_layout_set_height()
Havoc, I think that all you need here is setting height to G_MAXINT. -1 means "ellipsize to one line per paragraph" and that's for historical reasons. Just set G_MAXINT and wrapping wins over ellipsization.
That's btw what Nautilus does. It sets height to G_MAXINT in size_request and in size_allocate sets it to the actual allocation. I optimized PangoLayout to not do a full relayout if such a change doesn't make us have to ellipsize anything.
(In reply to comment #5) > That's btw what Nautilus does. It sets height to G_MAXINT in size_request and > in size_allocate sets it to the actual allocation. I optimized PangoLayout to > not do a full relayout if such a change doesn't make us have to ellipsize > anything. This was done in bug 549003 BTW.
Havoc, please reopen if you still think there's something to fix here.
MAXINT avoids this issue: * wrap=true, ellipsize=true does not do any wrapping if height=-1 but this still seems strange: * wrap=false, ellipsize=true does wrap if height is set I don't really know how to fix it in a compatible way though, so maybe not much to be done. I won't reopen if there aren't any ideas.
Havoc, there is no wrap=false in Pango. What do you mean?
Hmm, true, I guess I was thinking there was a WRAP_MODE_NONE. There's a ClutterLabel::wrap=true|false and similar for GtkLabel::wrap. Both also have ::ellipsize booleans. Let's see if I can remember what I meant ... If setting height is unsupported (GtkLabel status quo), then to turn off wrap you just don't set the width (or set it to maxint) in the size request. Then in size allocate, you can always set width, because: wrap=false, ellipsize=false: we'll have requested min size of entire needed width, so no wrap or ellipsize can happen wrap=false, ellipsize=true: setting ellipsize on PangoLayout prevents any wrapping if height is unset, so wrap=false honored wrap=true, ellipsize=false: disabling ellipsize ensures we only wrap, don't ellipsize, so wrap=true honored wrap=true, ellipsize=true: setting ellipsize on PangoLayout prevents any wrapping if height is unset, so wrap=true is irrelevant When adding height support, now in size allocate you can't unconditionally set height: wrap=false, ellipsize=false: we'll have requested min size of entire needed width and height, so no wrap or ellipsize happens (setting height is pointless but harmless) wrap=false, ellipsize=true: necessary to special-case this and *not* set the height, or else it will wrap (setting height breaks this case) wrap=true, ellipsize=false: disabling ellipsize ensures we only wrap, don't ellipsize (setting height pointless but harmless) wrap=true, ellipsize=true: setting height causes us to first wrap then ellipsize, as desired (setting height is necessary to get this behavior) This results in the following in ClutterLabel: + /* Pango only uses height if ellipsization is enabled, so don't set + * height if ellipsize isn't set. Pango implicitly enables wrapping + * if height is set, so don't set height if wrapping is disabled. + * In other words, only set height if we want to both wrap then + * ellipsize. + * See http://bugzilla.gnome.org/show_bug.cgi?id=560931 if this + * seems odd. + */ + if (allocation_height > 0 && + priv->wrap && + priv->ellipsize != PANGO_ELLIPSIZE_NONE) + { + gint height; + + height = allocation_height > 0 + ? CLUTTER_UNITS_TO_PANGO_UNIT (allocation_height) + : -1; + + pango_layout_set_height (layout, height); + } (In the clutter code, allocation_height < 0 means "size request" and allocation_height >= 0 means "size allocate") While with width, you just pass the allocation through to the PangoLayout by setting the allocated width, always. I don't know, it seems to be quite confusing somehow when combined with the ClutterLabel and GtkLabel APIs. But I did get it to work now. I guess a fix might require a wrap=false setting on layout. It isn't that important...
Ok, I see. I have considered WRAP_NONE before and thought that enum is too small to add to at this point. But thinking again now, I actually want to do it. It won't be default, so you don't get it back unless you actually set it.
-- GitLab Migration Automatic Message -- This bug has been migrated to GNOME's GitLab instance and has been closed from further activity. You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.gnome.org/GNOME/pango/issues/141.