Skip to content
This repository has been archived by the owner on Aug 6, 2023. It is now read-only.

feat(layout): add support for multiple weighted constraints by chunks #519

Closed
wants to merge 1 commit into from

Conversation

fdehau
Copy link
Owner

@fdehau fdehau commented Aug 8, 2021

Description

Layout allows users to split a given Rect in multiple chunks according to list of Constraints. It has several shortcomings:

  • it allows a single constraints by chunk.
  • all constraints have the same weight so the underlying layout solver has to pick randomly which rule to break.
  • there is no way to tell to tui to have one chunk fill the remaining space — it only works in some cases because internally constraints are added to link the last chunk border and the edge of the area.

This PR proposes 2 changes:

  • to allow multiple constraints by chunk.
  • to let users weight their constraints in order to control the order in which constraints get broken.

For example, let's say you have a table with 3 colums and you want the following behavior:

  • width of the first column should always be 10
  • width of the second column should be 20
  • the third column should fill the rest of the space
  • if the total width is reduced, the third columns shrinks until 20, then the second column until 10, then the third to 0, then the second to 0 and finally the first one to 0.

This can expressed as the following with the new changes — the higher the weight the stronger the constraint:

Table::new()
  .widths([
    // first column
    Constraint::eq(10).weight(50),
    // second column
    Constraints::from_iter([
      Constraint::gte(10).weight(40),
      Constraint::eq(20).weight(20)
    ]),
    // third column
    Constraint::from_iter([
      Constraint::gte(20).weight(30),
      Constraint::eq(Unit::Percentage(100)).weight(10),
    ])
  ]);

Known limitations: because we don't have a way to size based on content Constraint::gte(0) alone produces a zero width result which might not be expected from people used to more traditional layout system.

Testing guidelines

Checklist

@medwards
Copy link

medwards commented Aug 8, 2021

I like the sentiment here, and it would definitely help with some use-cases, but not all layout use-cases. For example, what if I have length constraints for each column, but want the right-most column to be right-aligned? This is possible with cassowary-rs (box2.right |EQ(REQUIRED)| window_width, // right align) but not with partial exposure of its options. Having dug into it more cassowary-rs can express an astounding number of relationships, consider also (box1.right - box1.left) / 50.0 |EQ(MEDIUM)| (box2.right - box2.left) / 100.0 which specifies a relationship across the column widths (one column is always half the width of the other).

There's an obvious problem exposing cassowary-rs constraints directly, namely that your crates version is now tied to the crates version, however cassowary-rs hasn't been updated in years so that's probably not a big pain point, especially when compared to trying to expose the expressiveness of the library. Another option is to just provide a way to add your own constraints that overrides the tui-rs list of Constraint.

Within the current prototype, Constraint::from_iter is obviously vital to be able to specify per-chunk constraints. I still think you would want to consider a Constraint::cassowary(left, right, right - left | LE(Required) | 5 (where left and right were assigned to Variable beforehand).

In general, I think folks will periodically want access to the full constraint solver and only partially exposing it would be a pain point. I'm not sure how to attach the Variable to the right thing in the constraint (especially window_width).

because we don't have a way to size based on content Constraint::gte(0) alone produces a zero width result which might not be expected from people used to more traditional layout system.

Mentioning size based on content is super interesting. I ran into this /w my hacked up solver: Because some of my columns are right aligned I have to "fill in" the gap in between the left/right aligned columns. I just assign all of the space to the left column, but if I had a variable representing the contents width I might be able to write a constraint that figures out a smarter way to share that space between columns. My intuition based on this is exposing interesting variables to the constraint solver lets authors write advanced constraints more easily and this is a big value-add over cassowary-rs alone.

fdehau added a commit that referenced this pull request Nov 21, 2021
Third column in table example was using the `Max` constraint.

But since version 0.16, the layout system does not add a hidden constraint on the last column which would ensure that it fills the remaining available space (a change that was already mentioned in #525). In addition, `tui` does not support sizing based on content because of its immediate mode nature. Therefore, `Max` is now resolved to `0`. Replacing with `Min` fixes the issue.

A new way of specifying constraints is being worked on at #519 which should for more deterministic and advanced layout.
@fdehau fdehau mentioned this pull request Nov 21, 2021
3 tasks
fdehau added a commit that referenced this pull request Nov 21, 2021
Third column in table example was using the `Max` constraint.

But since version 0.16, the layout system does not add a hidden constraint on the last column which would ensure that it fills the remaining available space (a change that was already mentioned in #525). In addition, `tui` does not support sizing based on content because of its immediate mode nature. Therefore, `Max` is now resolved to `0`. Replacing with `Min` fixes the issue.

A new way of specifying constraints is being worked on at #519 which should for more deterministic and advanced layout.
@remmycat remmycat mentioned this pull request Mar 5, 2022
3 tasks
@fdehau fdehau closed this Aug 6, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants