Implement Container widget

Remove `align_self` and `justify_content` methods
This commit is contained in:
Héctor Ramón Jiménez 2019-11-11 05:26:08 +01:00
parent bfe19193b9
commit ceb02f4a36
25 changed files with 310 additions and 205 deletions

View file

@ -15,7 +15,4 @@ pub enum Align {
/// Align at the end of the cross axis.
End,
/// Stretch over the cross axis.
Stretch,
}

View file

@ -1,27 +0,0 @@
/// Distribution on the main axis of a container.
///
/// * On a [`Column`], it describes __vertical__ distribution.
/// * On a [`Row`], it describes __horizontal__ distribution.
///
/// [`Column`]: widget/struct.Column.html
/// [`Row`]: widget/struct.Row.html
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Justify {
/// Place items at the start of the main axis.
Start,
/// Place items at the center of the main axis.
Center,
/// Place items at the end of the main axis.
End,
/// Place items with space between.
SpaceBetween,
/// Place items with space around.
SpaceAround,
/// Place items with evenly distributed space.
SpaceEvenly,
}

View file

@ -3,7 +3,6 @@ pub mod widget;
mod align;
mod background;
mod color;
mod justify;
mod length;
mod point;
mod rectangle;
@ -12,7 +11,6 @@ mod vector;
pub use align::Align;
pub use background::Background;
pub use color::Color;
pub use justify::Justify;
pub use length::Length;
pub use point::Point;
pub use rectangle::Rectangle;

View file

@ -9,6 +9,7 @@
//! ```
mod checkbox;
mod column;
mod container;
mod image;
mod radio;
mod row;
@ -32,6 +33,7 @@ pub use text_input::TextInput;
pub use checkbox::Checkbox;
pub use column::Column;
pub use container::Container;
pub use image::Image;
pub use radio::Radio;
pub use row::Row;

View file

@ -5,7 +5,7 @@
//! [`Button`]: struct.Button.html
//! [`State`]: struct.State.html
use crate::{Align, Background, Length};
use crate::{Background, Length};
/// A generic widget that produces a message when clicked.
pub struct Button<'a, Message, Element> {
@ -24,8 +24,6 @@ pub struct Button<'a, Message, Element> {
pub background: Option<Background>,
pub border_radius: u16,
pub align_self: Option<Align>,
}
impl<'a, Message, Element> std::fmt::Debug for Button<'a, Message, Element>
@ -57,7 +55,6 @@ impl<'a, Message, Element> Button<'a, Message, Element> {
padding: 0,
background: None,
border_radius: 0,
align_self: None,
}
}
@ -84,17 +81,6 @@ impl<'a, Message, Element> Button<'a, Message, Element> {
self
}
/// Sets the alignment of the [`Button`] itself.
///
/// This is useful if you want to override the default alignment given by
/// the parent container.
///
/// [`Button`]: struct.Button.html
pub fn align_self(mut self, align: Align) -> Self {
self.align_self = Some(align);
self
}
/// Sets the message that will be produced when the [`Button`] is pressed.
///
/// [`Button`]: struct.Button.html

View file

@ -1,4 +1,4 @@
use crate::{Align, Justify, Length};
use crate::{Align, Length};
use std::u32;
@ -14,9 +14,7 @@ pub struct Column<Element> {
pub height: Length,
pub max_width: u32,
pub max_height: u32,
pub align_self: Option<Align>,
pub align_items: Align,
pub justify_content: Justify,
pub children: Vec<Element>,
}
@ -32,9 +30,7 @@ impl<Element> Column<Element> {
height: Length::Shrink,
max_width: u32::MAX,
max_height: u32::MAX,
align_self: None,
align_items: Align::Start,
justify_content: Justify::Start,
children: Vec::new(),
}
}
@ -89,17 +85,6 @@ impl<Element> Column<Element> {
self
}
/// Sets the alignment of the [`Column`] itself.
///
/// This is useful if you want to override the default alignment given by
/// the parent container.
///
/// [`Column`]: struct.Column.html
pub fn align_self(mut self, align: Align) -> Self {
self.align_self = Some(align);
self
}
/// Sets the horizontal alignment of the contents of the [`Column`] .
///
/// [`Column`]: struct.Column.html
@ -108,15 +93,6 @@ impl<Element> Column<Element> {
self
}
/// Sets the vertical distribution strategy for the contents of the
/// [`Column`] .
///
/// [`Column`]: struct.Column.html
pub fn justify_content(mut self, justify: Justify) -> Self {
self.justify_content = justify;
self
}
/// Adds an element to the [`Column`].
///
/// [`Column`]: struct.Column.html

View file

@ -0,0 +1,94 @@
use crate::{Align, Length};
use std::u32;
#[derive(Debug)]
pub struct Container<Element> {
pub width: Length,
pub height: Length,
pub max_width: u32,
pub max_height: u32,
pub padding: u16,
pub horizontal_alignment: Align,
pub vertical_alignment: Align,
pub content: Element,
}
impl<Element> Container<Element> {
/// Creates an empty [`Container`].
///
/// [`Container`]: struct.Container.html
pub fn new<T>(content: T) -> Self
where
T: Into<Element>,
{
Container {
width: Length::Shrink,
height: Length::Shrink,
max_width: u32::MAX,
max_height: u32::MAX,
horizontal_alignment: Align::Start,
vertical_alignment: Align::Start,
padding: 0,
content: content.into(),
}
}
/// Sets the width of the [`Container`].
///
/// [`Container`]: struct.Container.html
pub fn width(mut self, width: Length) -> Self {
self.width = width;
self
}
/// Sets the height of the [`Container`].
///
/// [`Container`]: struct.Container.html
pub fn height(mut self, height: Length) -> Self {
self.height = height;
self
}
/// Sets the maximum width of the [`Container`].
///
/// [`Container`]: struct.Container.html
pub fn max_width(mut self, max_width: u32) -> Self {
self.max_width = max_width;
self
}
/// Sets the maximum height of the [`Container`] in pixels.
///
/// [`Container`]: struct.Container.html
pub fn max_height(mut self, max_height: u32) -> Self {
self.max_height = max_height;
self
}
/// Sets the padding of the [`Container`].
///
/// [`Container`]: struct.Container.html
pub fn padding(mut self, units: u16) -> Self {
self.padding = units;
self
}
/// Centers the contents in the horizontal axis of the [`Container`].
///
/// [`Container`]: struct.Container.html
pub fn center_x(mut self) -> Self {
self.horizontal_alignment = Align::Center;
self
}
/// Centers the contents in the vertical axis of the [`Container`].
///
/// [`Container`]: struct.Container.html
pub fn center_y(mut self) -> Self {
self.vertical_alignment = Align::Center;
self
}
}

View file

@ -24,8 +24,6 @@ pub struct Image {
/// The height of the image
pub height: Length,
pub align_self: Option<Align>,
}
impl Image {
@ -38,7 +36,6 @@ impl Image {
clip: None,
width: Length::Shrink,
height: Length::Shrink,
align_self: None,
}
}
@ -65,15 +62,4 @@ impl Image {
self.height = height;
self
}
/// Sets the alignment of the [`Image`] itself.
///
/// This is useful if you want to override the default alignment given by
/// the parent container.
///
/// [`Image`]: struct.Image.html
pub fn align_self(mut self, align: Align) -> Self {
self.align_self = Some(align);
self
}
}

View file

@ -1,4 +1,4 @@
use crate::{Align, Justify, Length};
use crate::{Align, Length};
use std::u32;
@ -14,9 +14,7 @@ pub struct Row<Element> {
pub height: Length,
pub max_width: u32,
pub max_height: u32,
pub align_self: Option<Align>,
pub align_items: Align,
pub justify_content: Justify,
pub children: Vec<Element>,
}
@ -32,9 +30,7 @@ impl<Element> Row<Element> {
height: Length::Shrink,
max_width: u32::MAX,
max_height: u32::MAX,
align_self: None,
align_items: Align::Start,
justify_content: Justify::Start,
children: Vec::new(),
}
}
@ -89,17 +85,6 @@ impl<Element> Row<Element> {
self
}
/// Sets the alignment of the [`Row`] itself.
///
/// This is useful if you want to override the default alignment given by
/// the parent container.
///
/// [`Row`]: struct.Row.html
pub fn align_self(mut self, align: Align) -> Self {
self.align_self = Some(align);
self
}
/// Sets the vertical alignment of the contents of the [`Row`] .
///
/// [`Row`]: struct.Row.html
@ -108,15 +93,6 @@ impl<Element> Row<Element> {
self
}
/// Sets the horizontal distribution strategy for the contents of the
/// [`Row`] .
///
/// [`Row`]: struct.Row.html
pub fn justify_content(mut self, justify: Justify) -> Self {
self.justify_content = justify;
self
}
/// Adds an [`Element`] to the [`Row`].
///
/// [`Element`]: ../struct.Element.html

View file

@ -7,7 +7,6 @@ pub struct Scrollable<'a, Element> {
pub state: &'a mut State,
pub height: Length,
pub max_height: u32,
pub align_self: Option<Align>,
pub content: Column<Element>,
}
@ -17,7 +16,6 @@ impl<'a, Element> Scrollable<'a, Element> {
state,
height: Length::Shrink,
max_height: u32::MAX,
align_self: None,
content: Column::new(),
}
}
@ -72,17 +70,6 @@ impl<'a, Element> Scrollable<'a, Element> {
self
}
/// Sets the alignment of the [`Scrollable`] itself.
///
/// This is useful if you want to override the default alignment given by
/// the parent container.
///
/// [`Scrollable`]: struct.Scrollable.html
pub fn align_self(mut self, align: Align) -> Self {
self.align_self = Some(align);
self
}
/// Sets the horizontal alignment of the contents of the [`Scrollable`] .
///
/// [`Scrollable`]: struct.Scrollable.html
@ -142,9 +129,9 @@ impl State {
pub fn offset(&self, bounds: Rectangle, content_bounds: Rectangle) -> u32 {
let hidden_content =
(content_bounds.height - bounds.height).round() as u32;
(content_bounds.height - bounds.height).max(0.0).round() as u32;
self.offset.min(hidden_content).max(0)
self.offset.min(hidden_content)
}
pub fn is_scrollbar_grabbed(&self) -> bool {

View file

@ -1,6 +1,6 @@
use iced::{
scrollable, text::HorizontalAlignment, text_input, Align, Application,
Checkbox, Color, Column, Element, Scrollable, Text, TextInput,
scrollable, text::HorizontalAlignment, text_input, Application, Checkbox,
Color, Column, Container, Element, Length, Scrollable, Text, TextInput,
};
pub fn main() {
@ -78,7 +78,6 @@ impl Application for Todos {
let content = Column::new()
.max_width(800)
.align_self(Align::Center)
.spacing(20)
.push(title)
.push(input)
@ -86,7 +85,7 @@ impl Application for Todos {
Scrollable::new(&mut self.scroll)
.padding(40)
.push(content)
.push(Container::new(content).width(Length::Fill).center_x())
.into()
}
}

View file

@ -1,7 +1,7 @@
use iced::{
button, scrollable, slider, text::HorizontalAlignment, text_input, Align,
Application, Background, Button, Checkbox, Color, Column, Element, Image,
Justify, Length, Radio, Row, Scrollable, Slider, Text, TextInput,
Application, Background, Button, Checkbox, Color, Column, Container,
Element, Image, Length, Radio, Row, Scrollable, Slider, Text, TextInput,
};
pub fn main() {
@ -78,7 +78,7 @@ impl Application for Tour {
);
}
let element: Element<_> = Column::new()
let content: Element<_> = Column::new()
.max_width(540)
.spacing(20)
.padding(20)
@ -86,20 +86,18 @@ impl Application for Tour {
.push(controls)
.into();
let element = if self.debug {
element.explain(Color::BLACK)
let content = if self.debug {
content.explain(Color::BLACK)
} else {
element
content
};
Column::new()
let scrollable = Scrollable::new(scroll)
.push(Container::new(content).width(Length::Fill).center_x());
Container::new(scrollable)
.height(Length::Fill)
.justify_content(Justify::Center)
.push(
Scrollable::new(scroll)
.align_items(Align::Center)
.push(element),
)
.center_y()
.into()
}
}
@ -340,10 +338,7 @@ impl<'a> Step {
}
fn container(title: &str) -> Column<'a, StepMessage> {
Column::new()
.spacing(20)
.align_items(Align::Stretch)
.push(Text::new(title).size(50))
Column::new().spacing(20).push(Text::new(title).size(50))
}
fn welcome() -> Column<'a, StepMessage> {
@ -646,19 +641,22 @@ impl<'a> Step {
}
}
fn ferris(width: u16) -> Image {
// This should go away once we unify resource loading on native
// platforms
if cfg!(target_arch = "wasm32") {
Image::new("resources/ferris.png")
} else {
Image::new(format!(
"{}/examples/resources/ferris.png",
env!("CARGO_MANIFEST_DIR")
))
}
.width(Length::Units(width))
.align_self(Align::Center)
fn ferris<'a>(width: u16) -> Container<'a, StepMessage> {
Container::new(
// This should go away once we unify resource loading on native
// platforms
if cfg!(target_arch = "wasm32") {
Image::new("resources/ferris.png")
} else {
Image::new(format!(
"{}/examples/resources/ferris.png",
env!("CARGO_MANIFEST_DIR")
))
}
.width(Length::Units(width)),
)
.width(Length::Fill)
.center_x()
}
fn button<'a, Message>(

View file

@ -17,7 +17,7 @@
// limitations under the License.
use crate::{
layout::{Limits, Node},
Element, Size,
Align, Element, Size,
};
#[derive(Debug)]
@ -56,6 +56,7 @@ pub fn resolve<Message, Renderer>(
limits: &Limits,
padding: f32,
spacing: f32,
align_items: Align,
children: &[Element<'_, Message, Renderer>],
) -> Node
where
@ -66,6 +67,7 @@ where
let mut total_non_fill =
spacing as f32 * (children.len() as i32 - 1).max(0) as f32;
let mut fill_sum = 0;
let mut cross = axis.cross(limits.min());
let mut nodes: Vec<Node> = Vec::with_capacity(children.len());
nodes.resize(children.len(), Node::default());
@ -81,8 +83,10 @@ where
let child_limits = Limits::new(Size::ZERO, limits.max());
let layout = child.layout(renderer, &child_limits);
let size = layout.size();
total_non_fill += axis.main(layout.size());
total_non_fill += axis.main(size);
cross = cross.max(axis.cross(size));
nodes[i] = layout;
} else {
@ -120,13 +124,13 @@ where
);
let layout = child.layout(renderer, &child_limits);
cross = cross.max(axis.cross(layout.size()));
nodes[i] = layout;
}
}
let mut main = padding;
let mut cross = axis.cross(limits.min());
for (i, node) in nodes.iter_mut().enumerate() {
if i > 0 {
@ -138,10 +142,18 @@ where
node.bounds.x = x;
node.bounds.y = y;
match axis {
Axis::Horizontal => {
node.align(Align::Start, align_items, Size::new(0.0, cross));
}
Axis::Vertical => {
node.align(align_items, Align::Start, Size::new(cross, 0.0));
}
}
let size = node.size();
main += axis.main(size);
cross = cross.max(axis.cross(size));
}
let (width, height) = axis.pack(main, cross);

View file

@ -22,6 +22,14 @@ impl Limits {
}
}
pub fn min(&self) -> Size {
self.min
}
pub fn max(&self) -> Size {
self.max
}
pub fn width(mut self, width: Length) -> Limits {
match width {
Length::Shrink => {
@ -85,27 +93,6 @@ impl Limits {
self
}
pub fn resolve(&self, intrinsic_size: Size) -> Size {
Size::new(
intrinsic_size
.width
.min(self.max.width)
.max(self.fill.width),
intrinsic_size
.height
.min(self.max.height)
.max(self.fill.height),
)
}
pub fn min(&self) -> Size {
self.min
}
pub fn max(&self) -> Size {
self.max
}
pub fn pad(&self, padding: f32) -> Limits {
self.shrink(Size::new(padding * 2.0, padding * 2.0))
}
@ -128,4 +115,25 @@ impl Limits {
Limits { min, max, fill }
}
pub fn loose(&self) -> Limits {
Limits {
min: Size::ZERO,
max: self.max,
fill: self.fill,
}
}
pub fn resolve(&self, intrinsic_size: Size) -> Size {
Size::new(
intrinsic_size
.width
.min(self.max.width)
.max(self.fill.width),
intrinsic_size
.height
.min(self.max.height)
.max(self.fill.height),
)
}
}

View file

@ -1,4 +1,4 @@
use crate::{Rectangle, Size};
use crate::{Align, Rectangle, Size};
#[derive(Debug, Clone, Default)]
pub struct Node {
@ -34,4 +34,27 @@ impl Node {
pub fn children(&self) -> &[Node] {
&self.children
}
pub fn align(
&mut self,
horizontal_alignment: Align,
vertical_alignment: Align,
space: Size,
) {
match horizontal_alignment {
Align::Start => {}
Align::Center => {
self.bounds.x += (space.width - self.bounds.width) / 2.0;
}
Align::End => {}
}
match vertical_alignment {
Align::Start => {}
Align::Center => {
self.bounds.y += (space.height - self.bounds.height) / 2.0;
}
Align::End => {}
}
}
}

View file

@ -211,7 +211,7 @@ mod size;
mod user_interface;
pub use iced_core::{
Align, Background, Color, Justify, Length, Point, Rectangle, Vector,
Align, Background, Color, Length, Point, Rectangle, Vector,
};
pub use element::Element;

View file

@ -31,6 +31,8 @@ pub mod slider;
pub mod text;
pub mod text_input;
mod container;
#[doc(no_inline)]
pub use button::Button;
#[doc(no_inline)]
@ -38,6 +40,8 @@ pub use checkbox::Checkbox;
#[doc(no_inline)]
pub use column::Column;
#[doc(no_inline)]
pub use container::Container;
#[doc(no_inline)]
pub use image::Image;
#[doc(no_inline)]
pub use radio::Radio;

View file

@ -7,7 +7,7 @@
//! [`Class`]: enum.Class.html
use crate::input::{mouse, ButtonState};
use crate::{layout, Element, Event, Hasher, Layout, Length, Point, Widget};
use crate::{layout, Element, Event, Hasher, Layout, Point, Widget};
use std::hash::Hash;
pub use iced_core::button::State;
@ -78,7 +78,6 @@ where
fn hash_layout(&self, state: &mut Hasher) {
self.width.hash(state);
self.align_self.hash(state);
self.content.hash_layout(state);
}
}

View file

@ -1,8 +1,6 @@
use std::hash::Hash;
use crate::{
layout, Element, Event, Hasher, Layout, Length, Point, Size, Widget,
};
use crate::{layout, Element, Event, Hasher, Layout, Length, Point, Widget};
/// A container that distributes its contents vertically.
pub type Column<'a, Message, Renderer> =
@ -34,6 +32,7 @@ where
&limits,
self.padding as f32,
self.spacing as f32,
self.align_items,
&self.children,
)
}
@ -74,9 +73,7 @@ where
self.height.hash(state);
self.max_width.hash(state);
self.max_height.hash(state);
self.align_self.hash(state);
self.align_items.hash(state);
self.justify_content.hash(state);
self.spacing.hash(state);
for child in &self.children {

View file

@ -0,0 +1,93 @@
use std::hash::Hash;
use crate::{
layout, Element, Event, Hasher, Layout, Length, Point, Size, Widget,
};
/// A container that distributes its contents vertically.
pub type Container<'a, Message, Renderer> =
iced_core::Container<Element<'a, Message, Renderer>>;
impl<'a, Message, Renderer> Widget<Message, Renderer>
for Container<'a, Message, Renderer>
where
Renderer: crate::Renderer,
{
fn width(&self) -> Length {
self.width
}
fn layout(
&self,
renderer: &Renderer,
limits: &layout::Limits,
) -> layout::Node {
let limits = limits
.loose()
.max_width(self.max_width)
.max_height(self.max_height)
.width(self.width)
.height(self.height);
let mut content = self.content.layout(renderer, &limits);
let size = limits.resolve(content.size());
content.align(self.horizontal_alignment, self.vertical_alignment, size);
layout::Node::with_children(size, vec![content])
}
fn on_event(
&mut self,
event: Event,
layout: Layout<'_>,
cursor_position: Point,
messages: &mut Vec<Message>,
renderer: &Renderer,
) {
self.content.widget.on_event(
event,
layout.children().next().unwrap(),
cursor_position,
messages,
renderer,
)
}
fn draw(
&self,
renderer: &mut Renderer,
layout: Layout<'_>,
cursor_position: Point,
) -> Renderer::Output {
self.content.draw(
renderer,
layout.children().next().unwrap(),
cursor_position,
)
}
fn hash_layout(&self, state: &mut Hasher) {
0.hash(state);
self.width.hash(state);
self.height.hash(state);
self.max_width.hash(state);
self.max_height.hash(state);
self.padding.hash(state);
self.content.hash_layout(state);
}
}
impl<'a, Message, Renderer> From<Container<'a, Message, Renderer>>
for Element<'a, Message, Renderer>
where
Renderer: 'a + crate::Renderer,
Message: 'static,
{
fn from(
column: Container<'a, Message, Renderer>,
) -> Element<'a, Message, Renderer> {
Element::new(column)
}
}

View file

@ -30,7 +30,6 @@ where
fn hash_layout(&self, state: &mut Hasher) {
self.width.hash(state);
self.height.hash(state);
self.align_self.hash(state);
}
}

View file

@ -32,6 +32,7 @@ where
&limits,
self.padding as f32,
self.spacing as f32,
self.align_items,
&self.children,
)
}
@ -72,9 +73,7 @@ where
self.height.hash(state);
self.max_width.hash(state);
self.max_height.hash(state);
self.align_self.hash(state);
self.align_items.hash(state);
self.justify_content.hash(state);
self.spacing.hash(state);
self.spacing.hash(state);

View file

@ -168,7 +168,6 @@ where
self.height.hash(state);
self.max_height.hash(state);
self.align_self.hash(state);
self.content.hash_layout(state)
}

View file

@ -2,11 +2,11 @@ pub use iced_wgpu::{Primitive, Renderer};
pub use iced_winit::{
button, scrollable, slider, text, text_input, winit, Align, Background,
Checkbox, Color, Image, Justify, Length, Radio, Scrollable, Slider, Text,
TextInput,
Checkbox, Color, Image, Length, Radio, Scrollable, Slider, Text, TextInput,
};
pub type Element<'a, Message> = iced_winit::Element<'a, Message, Renderer>;
pub type Container<'a, Message> = iced_winit::Container<'a, Message, Renderer>;
pub type Row<'a, Message> = iced_winit::Row<'a, Message, Renderer>;
pub type Column<'a, Message> = iced_winit::Column<'a, Message, Renderer>;
pub type Button<'a, Message> = iced_winit::Button<'a, Message, Renderer>;

View file

@ -1,12 +1,13 @@
use crate::{
column, conversion,
conversion,
input::{keyboard, mouse},
renderer::{Target, Windowed},
Cache, Column, Debug, Element, Event, Length, MouseCursor, UserInterface,
Cache, Container, Debug, Element, Event, Length, MouseCursor,
UserInterface,
};
pub trait Application {
type Renderer: Windowed + column::Renderer;
type Renderer: Windowed;
type Message: std::fmt::Debug;
@ -282,9 +283,8 @@ where
let view = application.view();
debug.view_finished();
Column::new()
Container::new(view)
.width(Length::Units(size.width.round() as u16))
.height(Length::Units(size.height.round() as u16))
.push(view)
.into()
}