Layouts
Types of Layouts
The common types of layouts are vstack
, hstack
, zstack
, and
flexstack
. The first three are more or less straightforward, but flexstack
takes from
Heterogenous layouts
For each of these, you can layout a heterogenous collection of elements.
#![allow(unused)] fn main() { // vstack() is an alias for VStack::hetero() VStack::hetero() .push(first_view) .push(second_view) ... }
Iterator
If you have a collection of items, and for each one you want to display
a view based off of that item,
you can use the vmap
, hmap
, zmap
, and flexmap
functions.
Here's a toy example.
#![allow(unused)] fn main() { let strings = ["The", "rabbit", "jumped", "over", "the", "moon"]; strings .vmap(|string| text(string)); }
Binding
If you have a binding of a vector of data, you can have a layout of the mapped views, which updates automatically as the binding updates.
#![allow(unused)] fn main() { let strings: impl Binding<Filterless<Vec<String>>>; strings .binding_vmap(|string| text(string)); }
Signal
Finally, there's the analogous method for signals of vectors. If you can,
try to use binding_vmap
or its analogs instead as sig_vmap
is significantly
slower.
#![allow(unused)] fn main() { let strings: impl Signal<Target=Vec<String>>; strings .sig_vmap(|string| text(string)); }
Using Options
Sometimes you want to customize a vstack to adjust the spacing, for example. This can be done using the options for that layout.
#![allow(unused)] fn main() { VStack::hetero_options( VStackOptions::default() .direction(VerticalDirection::Up) .align(HorizontalAlignment::Trailing) .spacing(0.0) ) .push(...) // signal signal.sig_vmap_options(|elem| ..., VStackOptions::default() .spacing(0.0) ) // iterator into_iteratoe.vmap_options(|elem| ..., VStackOptions::default() .spacing(0.0) ) // binding binding.binding_vmap_options(|elem| ..., VStackOptions::default() .spacing(0.0) ) }
FlexStack
The behavior of vstack
, hstack
, and zstack
are more or less intuitive,
but flexstack
is slightly more complex. It is based off html flexbox
and most features present in the html edition are present here as well.
In the options of the parent view, you can specify things such
as the flex direction, whether it wraps, etc.
For each subview, you can use the flex
modifier to set options about how this
specific child should be sized.
#![allow(unused)] fn main() { FlexStack::hetero_options(FlexStackOptions::default().gap(0.0)) .push( text("first") .frame(F.intrinsic(200, 30).squished(40, 30).unlimited_stretch()) .border(RED, 1) .flex(FlexContext::default().grow(1.0)) ) .push( text("second block of text") .frame(F.intrinsic(100, 30).unlimited_stretch()) .border(BLUE, 1) .flex(FlexContext::default().grow(3.0)) ) .push( text("final") .frame(F.intrinsic(300, 30).unlimited_stretch()) .border(BLACK, 1) .flex(FlexContext::default().grow(0.5)) ); }
See more options in use in the flex example.
Remark: one of the nice things about Quarve is that
the layout system is general enough that flexstack
doesn't
have to be axiomatized,
i.e. flexstack doesn't use any internal-only APIs
so a user using the quarve library could fully recreate flexstack and theoretically
more complex generalizations.
HSplit
You can also easily create a vertical/horizontal split view. It's generally recommended to put each of the left and right into their own frames so that you can be explicit about the minimum and maximum content size.
#![allow(unused)] fn main() { HSplit::new(left, right) }
LayoutProvider
What if you have a set of views and you need to layout the children in
some complex manner. One option is to manually implement view provider
and handle all details of the layout process. However, a slightly easier way
is to implement LayoutProvider
instead.
Given any struct that implements layout provider, you can convert it into
a view provider by using the .into_layout_view_provider
method.
This handles the native backing automatically. Apart from that, the
methods and layout process is more or less the same as with vanilla ViewProvider
(revisit that lesson if needed).
Note that another common use case of LayoutProvider
is to handle mouse events,
which is otherwise difficult to do.
In fact, HSplit
and VSplit
use a LayoutProvider
under the hood.
VecLayoutProvider (Advanced)
Note that layout providers such as vstack
, hstack
, zstack
, flexstack
, etc
can be defined on any number of subviews.
For these, it would be nice to have a simple interface for performing the layout
and automatically creating the relevant hetero
, vmap
, sig_vmap
, etc methods.
Indeed, you can implement the VecLayoutProvider
yourself to specify how to layout
an arbitrary number of subviews. To get the relevant methods, quarve provides
a set of macros, such as impl_hetero_layout
to make it quite simple.
See how vstack and others are implemented
here
(you may have to scroll quite far, it's in the vec_layout::vstack
module)