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

Introduce a vertical/horizontal split layout #1261

Merged
merged 13 commits into from Oct 29, 2019

Conversation

DaveShuckerow
Copy link
Contributor

@DaveShuckerow DaveShuckerow commented Oct 25, 2019

Fixes #1183.

TODO:

  • Use a proper decoration for the divider thumb
  • Test to be sure we don't impose negative constraints on children in vertical or horizontal mode

@DaveShuckerow DaveShuckerow added the hummingbird Hummingbird implementation-related label Oct 25, 2019
@@ -113,3 +116,163 @@ class DefaultTaggedText extends StatelessWidget {
);
}
}

Copy link
Contributor

Choose a reason for hiding this comment

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

maybe move this widget over to its own file under src/flutter. It is complex enough to be worth its own file. I would propose keeping comm_widgets.dart for widgets that don't really have complex business logic but are good to share.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

SGTM, moved.

@@ -113,3 +116,163 @@ class DefaultTaggedText extends StatelessWidget {
);
}
}

/// A widget that takes two children and lays them out along one axis.
Copy link
Contributor

Choose a reason for hiding this comment

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

This summary is missing the key bit that the user resize the widgets along that axis.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch, done.

/// [firstChild] will be placed before [secondChild] in a [Row].
const Split.horizontal({
Key key,
@required Widget firstChild,
Copy link
Contributor

Choose a reason for hiding this comment

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

instead of firstChild and second child, why not use children:
If you only support two children you can always assert that there are exactly two children.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

firstChild and secondChild explain exactly what the code does without a need for asserts.

It's simpler, and this pattern has prior art.

Unless we plan on supporting n != 2 children, I'd like to leave this as-is.

Copy link
Contributor

Choose a reason for hiding this comment

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

my guess is at some point we will want to support n > 2 children.

Key key,
@required Widget firstChild,
@required Widget secondChild,
double initialFirstFraction,
Copy link
Contributor

Choose a reason for hiding this comment

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

you also need to track the minimum size allowed for each split component. that is critical to ensure that users don't accidentally get the UI in a weird state by collapsing one side of the split too much.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've left a TODO to follow up on this.

height: isHorizontal ? height : Split.dividerMainAxisSize,
child: const Center(
child: Text(
':::::::',
Copy link
Contributor

Choose a reason for hiding this comment

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

use the existing png for this instead of using text.
Alternately, find a more material design appropriate splitter icon.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Naturally we aren't going to land that :D

Do you know the name of the png file for the splitter grip?

Copy link
Contributor

Choose a reason for hiding this comment

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

The splitter icons are currently stored as data urls in the app as that is the typical way they are included as the images are tiny.
You can also find the pngs here.
https://github.com/nathancahill/split/tree/master/packages/splitjs/grips


.gutter.gutter-horizontal {
    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg==');
    cursor: col-resize;
    height: 100%;
}

.gutter.gutter-vertical {
    background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFAQMAAABo7865AAAABlBMVEVHcEzMzMzyAv2sAAAAAXRSTlMAQObYZgAAABBJREFUeF5jOAMEEAIEEFwAn3kMwcB6I2AAAAAASUVORK5CYII=');
    cursor: row-resize;
    width: 100%;
}

child: widget.secondChild,
),
];
if (widget.axis == Axis.horizontal) {
Copy link
Contributor

Choose a reason for hiding this comment

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

this can be replaced with
Flex(direction: widget.axis, children: children)

Column and Row are just sugar around Flex with a fixed direction.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good suggestion, done.

});
});

group('drags properly', () {
Copy link
Contributor

Choose a reason for hiding this comment

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

Great to see a test of this!

});
}

Widget _w1, _w2;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: instead of making _w1, _w2, and _split
surprise side effects of
buildSplitAndChildren why not use keys and lookup the widgets bsed on keys instead of using
find.byWidget?

final fractionalDelta = delta / axisSize;
setState(() {
// Update the fraction of space consumed by the children,
// being sure not to allocate any of them negative space.
Copy link
Contributor

Choose a reason for hiding this comment

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

here is where we need to instead clamp so no children violate their minSize constraint.

Copy link
Contributor

@jacob314 jacob314 left a comment

Choose a reason for hiding this comment

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

feel free to apply the feature request to specify a minWidth for each column as a followup cl.

this.firstChild,
this.secondChild,
double initialFirstFraction,
) : initialFirstFraction = initialFirstFraction ?? 0.5,
Copy link
Member

Choose a reason for hiding this comment

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

Does ??= work for this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No. This is equivalent to:

this.initialFirstFraction = initialFirstFraction ?? 0.5

The two variables are different, even though they have the same name.

const halfDivider = Split.dividerMainAxisSize / 2.0;
// The fraction of the layout the divider needs to take up from each child.
final halfDividerFraction = halfDivider / axisSize;

Copy link
Member

Choose a reason for hiding this comment

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

Why are we using fractions instead of just subtracting Split.dividerMainAxisSize / 2.0?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good suggestion, I've removed this field.

final halfDividerFraction = halfDivider / axisSize;

final sanitizedFirstFraction =
firstFraction.clamp(halfDividerFraction, 1.0 - halfDividerFraction);
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't the lower bound of the clamp be 0?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We need to make sure there is enough space to take halfDivider from each child, no matter how far we have slid the divider.

To do that, we have to clamp the available space to at least halfDivider for both children, then we can remove the halfDivider space.

I've updated the comments to explain, let me know if you have more questions. The geometry took a while to explain even explaining it to myself, so feedback here is appreciated.

@jacob314
Copy link
Contributor

One other high level comment. It would be nice to change the cursor matching how the cursor is changed by the existing UI when interacting with the spiller on desktop and web. If you want to do it with a follow up cl, feel free to add a tracking bug.

@DaveShuckerow
Copy link
Contributor Author

One other high level comment. It would be nice to change the cursor matching how the cursor is changed by the existing UI when interacting with the spiller on desktop and web. If you want to do it with a follow up cl, feel free to add a tracking bug.

#1265

@DaveShuckerow
Copy link
Contributor Author

Test run failures were caused by #1267 and #1266. This doesn't affect either, so I'm going to merge.

@DaveShuckerow DaveShuckerow merged commit 4384ed3 into flutter:master Oct 29, 2019
@DaveShuckerow DaveShuckerow deleted the split branch October 31, 2019 23:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
hummingbird Hummingbird implementation-related
Projects
No open projects
Development

Successfully merging this pull request may close these issues.

Support split layouts and resizing
3 participants