Xojo_constraintkit

Hi @Greg_O
Just downloaded your xojo_constraintkit. Thanks for making this available.

The macOS app within it does not contain the constraints you created during the webinar. this makes the example far less informative than it could be. You ended the Webinar with a significant working set of constrains on the window, but the current example only does a few of these things.

I just rewatched and there wasn’t much missing. I just coped them from the video. I had thought it had something to make labels right aligned, but it doesn’t seem to.

One thing that is a little odd, Label8 doesn’t seem to line up with the RadioButton.

OK, I’ve found a way of right aligning labels using the following two items:

MyLabel.UseIntrinsicWidth // fix the width to what is required
MyControl.AlignToLabel( MyLabel ) // align the label and control vertically by baseline
MyControl.LeftAnchor.ConstraintEqualToSystemSpacingAfterAnchor( MyLabel.RightAnchor ).Active = True // place the control just after the label

What I can’t figure out is how to make all the labels equal width to the longest, which could change by language. I suppose if I use:

MyLabel1.UseIntrinsicWidth
MyLabel2.UseIntrinsicWidth
MyLabel3.UseIntrinsicWidth

Var nMax as Integer = max( MyLabel1.Width, MyLabel2.Width, MyLabel3.Width )
// Then set each label width nMax, allowing the controls to move over to take account of the label width

Well, that doesn’t seem to work. Likely for 2 reasons:

  1. The .Width property doesn’t seem to be updated by the UseIntrinsicWidth
  2. Once the UseIntrinsicWidth constraint is applied it would likely override the attempt to change the widths.

Right. As soon as you start using constraints, you can basically ignore the “actual width” thing.

I’m not sure it’s worth trying to get all the labels to be the same width though. You could just add constraints so that all of the controls to the right of the labels have equal LeadingEdge constraints and let AutoLayout figure it out. You’d just need to tell the labels that they’re leadingEdge >= wherever your column should start.

I’m not near my computer at the moment but if I remember when I am, I can take a look.

I did notice that the IDE keeps making the Direction property of the UIStackView invisible so I may need to rename that property.

I probably should have mentioned that as soon as you convert a layout to autolayout, the Left, Top, Width and Height properties are no longer obeyed.

Basically I’m trying to create this sort of layout using constraints:

I want each of the left hand labels to be:

  1. Right aligned
  2. Equal width
  3. As long as the longest of them for the current given language

Thus avoiding the ‘German gap’ the English suffers from.

Obviously, I can offset the popup from the label (with standard gap), the segment from the popup with (standard gap). I can use intrinsic size to grow / shrink the labels, but I need ‘Sort by’ to be the same size as ‘Then sort by’.

This suggests a recipe. I’m trying to get that to work in this framework:

Anyhow, bed time now :slight_smile: No urgency at all.

Another interesting thought. If I have a container control, can I set constraints on the container itself to fit its parent window. I’m thinking of being able to make the container added to an Open/Save dialog, as an accessory view, be resizable with the dialog. Currently it is fixed and floats to the centre.

if you call ConvertToAutolayout on the container, it should technically just work. That is, the container will no longer have a fixed width. That said, centering the accessory view is probably the macOS default as I don’t seem to have called anything specific to make it do that.

Here’s an example project for getting the labels set properly:

https://www.dropbox.com/scl/fi/abijt5lwe8wtjxuyyj9t4/for-Ian.xojo_binary_project?rlkey=c2opn62judoepamjhw4bpckah&dl=1

Perfect, thank you. I’ve just tried it using multiple languages and it works a treat.

The only issue is that the labels are set to Right aligned, which I know I asked for. When you use an RTL language the alignment should be reversed. It is trivial to fix with a little code:

If SystemIsRTL Then
  Var oLabel As DesktopLabel
  For Each oControl As Object In Self.Controls
    If oControl IsA DesktopLabel Then
      
      oLabel = DesktopLabel( oControl )
      If oLabel.TextAlignment = TextAlignments.Right Then
        oLabel.TextAlignment = TextAlignments.Left
      Else
        oLabel.TextAlignment = TextAlignments.Right
      End If
      
    End If
  Next
End If

The code ignores the Default alignment option as it automatically adjusts to the correct orientation (Left on LTR and Right on RTL). However, Xojo is missing an option for “Contra-default”, as I think macOS is also.

I think that would a useful example for your system. It’s a relatively common layout and a good example of using your code.

Last nights exercise made me notice that the hugging and compression stuff wasn’t working on macOS so that’s been fixed n the repo and in that project. We might be able to turn off the alignment if the leading constraints for the labels were set to “greater than or equal” instead of just “equal”.

All excellent stuff. I keep getting caught on the fact that there is only a “ConstraintEqualToSystemSpacingAfterAnchor”, so when dealing with trailing edges you have to reverse the control / window arrangement. ie you have to place the window / container edge to be SystemSpacing after the control. and not the control SystemSpacing before the Window edge. Logically the same thing I know but semantically warps my mind.

One thing I’ve not quite figured yet is wether the Window .Height and .Width remain valid. I have certain dialog boxes that are resizable and that remember their last size and restore that for the next time. I’ve yet to see if that is maintained. Easy enough to test I suppose.

Just tried and it causes issues with the equal size requirement. The longer one is right aligned and the shorter one left aligned to the left of the longer one. It also causes the popups to crash down to minimum size.

I’ll look at that tonight if I have some time.

Thanks for that. I’m not asking for major time to be spent. I added an issue to your project about some issues with AlignToLabel.

I’m going to watch the video again, I’m attempting to setup a constraint that would limit the window size based on content and I’m sure you did that in the vid.

Even though I have constraints on the group box to the window, and the popups to the group box, I can still do this:

I also want it to work the other way, ie if it starts that way and the text becomes longer it expands the window to fit the contents. Again, I think you did this in the vid. I’ll watch again tomorrow. It is very likely Priorities.

So… this is one of those places where the order of the constraints matters. The fact that the pop up menu is going outside of the group probably means that you got it backwards.

Try making the constraint this way:

GroupBox1.TrailingAnchor.ConstraintEqualToSystemOffsetFromAnchor(popupmenu1.TrailingAnchor).Active = True

Remember, the offsets should be positive, not negative.

Thanks Greg, I’ll take a look tomorrow. I’m off at the cinema today (yes, all day :slight_smile:).

That’s what I already have:

Self.ConvertToAutolayout( True, True )

// set the existing constraints so they won't conflict with the ones we're creating below
Self.ConvertConstraintsForAllControls( 1 )

// 1. Remove the existing width constraints for the controls
'RemoveWidthConstraint( PopupMenu1 )
'RemoveWidthConstraint( PopupMenu2 )
RemoveWidthConstraint( Label1 )
RemoveWidthConstraint( Label2 )
RemoveWidthConstraint( GroupBox1 )

GroupBox1.LeadingAnchor.ConstraintEqualToSystemSpacingAfterAnchor( Self.LeadingAnchor ).Active = True
Self.TrailingAnchor.ConstraintEqualToSystemSpacingAfterAnchor( GroupBox1.TrailingAnchor ).Active = True

// 2. Bring the Hugging priority up for the labels so they're not allowed to expand
//      and the compression resistance up to prevent the labels from being compressed
Label1.HuggingPriorityForAxis( SOSConstraintKit.Axis.Horizontal ) = 999
Label2.HuggingPriorityForAxis( SOSConstraintKit.Axis.Horizontal ) = 999
Label1.CompressionResistancePriorityForAxis( SOSConstraintKit.Axis.Horizontal ) = 1000
Label2.CompressionResistancePriorityForAxis( SOSConstraintKit.Axis.Horizontal ) = 1000

// 3. Set the widths equal to one another
Label1.WidthAnchor.ConstraintEqualToAnchor( Label2.WidthAnchor ).Active = True

// 4. The controls on the right should be aligned to the labels
PopupMenu1.AlignToLabel( Label1 )
PopupMenu2.AlignToLabel( Label2 )

// 5. constrain the trailing edge of the popup menus against the trailing edge of view
GroupBox1.TrailingAnchor.ConstraintEqualToSystemSpacingAfterAnchor( PopupMenu1.TrailingAnchor ).Active = True
GroupBox1.TrailingAnchor.ConstraintEqualToSystemSpacingAfterAnchor( PopupMenu2.TrailingAnchor ).Active = True

// 6. constrain the leading edge of the labels to the leading edge of the view
Label1.LeadingAnchor.ConstraintEqualToSystemSpacingAfterAnchor( GroupBox1.LeadingAnchor ).Active = True
Label2.LeadingAnchor.ConstraintEqualToSystemSpacingAfterAnchor( GroupBox1.LeadingAnchor ).Active = True

So I have a constraint on the group box to the window, which works. I then want a constraint on the popup to the group box. Which doesn’t.

If I do the following

Self.TrailingAnchor.ConstraintEqualToSystemSpacingAfterAnchor( GroupBox1.TrailingAnchor ).Active = True
GroupBox1.TrailingAnchor.ConstraintSystemSpacingAfterAnchor( PopupMenu1.TrailingAnchor ).Active = True

Then the second is ignored. If I do the reverse, the group box becomes tiny.

GroupBox1.TrailingAnchor.ConstraintSystemSpacingAfterAnchor( PopupMenu1.TrailingAnchor ).Active = True
Self.TrailingAnchor.ConstraintEqualToSystemSpacingAfterAnchor( GroupBox1.TrailingAnchor ).Active = True

I’m curious… why did we decide that all of the labels needed to have the same width?