diff options
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | examples/ggez/main.rs | 21 | ||||
-rw-r--r-- | examples/ggez/renderer.rs | 26 | ||||
-rw-r--r-- | examples/ggez/renderer/button.rs | 1 | ||||
-rw-r--r-- | examples/ggez/renderer/debugger.rs | 30 | ||||
-rw-r--r-- | examples/ggez/renderer/image.rs | 51 | ||||
-rw-r--r-- | examples/ggez/renderer/text.rs | 17 | ||||
-rw-r--r-- | examples/ggez/tour.rs | 381 | ||||
-rw-r--r-- | examples/ggez/widget.rs | 3 | ||||
-rw-r--r-- | examples/resources/Roboto-LICENSE | 202 | ||||
-rw-r--r-- | examples/resources/Roboto-Regular.ttf | bin | 0 -> 171272 bytes | |||
-rw-r--r-- | examples/resources/ferris.png | bin | 0 -> 33061 bytes | |||
-rw-r--r-- | src/lib.rs | 4 | ||||
-rw-r--r-- | src/style.rs | 20 | ||||
-rw-r--r-- | src/widget.rs | 6 | ||||
-rw-r--r-- | src/widget/button.rs | 2 | ||||
-rw-r--r-- | src/widget/checkbox.rs | 2 | ||||
-rw-r--r-- | src/widget/column.rs | 10 | ||||
-rw-r--r-- | src/widget/image.rs | 63 | ||||
-rw-r--r-- | src/widget/progress_bar.rs | 2 | ||||
-rw-r--r-- | src/widget/radio.rs | 2 | ||||
-rw-r--r-- | src/widget/row.rs | 10 | ||||
-rw-r--r-- | src/widget/slider.rs | 2 | ||||
-rw-r--r-- | src/widget/text.rs | 18 |
24 files changed, 640 insertions, 235 deletions
@@ -19,4 +19,4 @@ twox-hash = "1.5" winit = { version = "0.20.0-alpha3", optional = true } [dev-dependencies] -ggez = { version = "0.5", git = "https://github.com/hecrj/ggez.git", branch = "font-cache" } +ggez = { version = "0.5", git = "https://github.com/hecrj/ggez.git" } diff --git a/examples/ggez/main.rs b/examples/ggez/main.rs index 329bde81..0a6a2005 100644 --- a/examples/ggez/main.rs +++ b/examples/ggez/main.rs @@ -16,8 +16,8 @@ pub fn main() -> ggez::GameResult { let (context, event_loop) = { &mut ggez::ContextBuilder::new("iced", "ggez") .window_mode(ggez::conf::WindowMode { - width: 1280.0, - height: 1024.0, + width: 850.0, + height: 850.0, ..ggez::conf::WindowMode::default() }) .build()? @@ -39,6 +39,7 @@ pub fn main() -> ggez::GameResult { struct Game { spritesheet: graphics::Image, + font: graphics::Font, tour: Tour, events: Vec<iced::Event>, @@ -51,7 +52,8 @@ impl Game { Ok(Game { spritesheet: graphics::Image::new(context, "/ui.png").unwrap(), - tour: Tour::new(), + font: graphics::Font::new(context, "/Roboto-Regular.ttf").unwrap(), + tour: Tour::new(context), events: Vec::new(), cache: Some(iced::Cache::default()), @@ -126,7 +128,7 @@ impl event::EventHandler for Game { } fn draw(&mut self, context: &mut ggez::Context) -> ggez::GameResult { - graphics::clear(context, [0.3, 0.3, 0.6, 1.0].into()); + graphics::clear(context, graphics::WHITE); let screen = graphics::screen_coordinates(context); @@ -134,14 +136,17 @@ impl event::EventHandler for Game { let layout = self.tour.layout(); let content = Column::new() - .width(screen.w as u32) - .height(screen.h as u32) + .width(screen.w as u16) + .height(screen.h as u16) .align_items(iced::Align::Center) .justify_content(iced::Justify::Center) .push(layout); - let renderer = - &mut Renderer::new(context, self.spritesheet.clone()); + let renderer = &mut Renderer::new( + context, + self.spritesheet.clone(), + self.font, + ); let mut ui = iced::UserInterface::build( content, diff --git a/examples/ggez/renderer.rs b/examples/ggez/renderer.rs index ccf12aba..8746dd96 100644 --- a/examples/ggez/renderer.rs +++ b/examples/ggez/renderer.rs @@ -1,24 +1,38 @@ mod button; mod checkbox; +mod debugger; +mod image; mod radio; mod slider; mod text; -use ggez::graphics::{self, spritebatch::SpriteBatch, Image}; +use ggez::graphics::{ + self, spritebatch::SpriteBatch, Font, Image, MeshBuilder, +}; use ggez::Context; pub struct Renderer<'a> { pub context: &'a mut Context, pub sprites: SpriteBatch, pub spritesheet: Image, + pub font: Font, + font_size: f32, + debug_mesh: Option<MeshBuilder>, } impl Renderer<'_> { - pub fn new(context: &mut Context, spritesheet: Image) -> Renderer { + pub fn new( + context: &mut Context, + spritesheet: Image, + font: Font, + ) -> Renderer { Renderer { context, sprites: SpriteBatch::new(spritesheet.clone()), spritesheet, + font, + font_size: 20.0, + debug_mesh: None, } } @@ -37,5 +51,13 @@ impl Renderer<'_> { graphics::FilterMode::Linear, ) .expect("Draw text"); + + if let Some(debug_mesh) = self.debug_mesh.take() { + let mesh = + debug_mesh.build(self.context).expect("Build debug mesh"); + + graphics::draw(self.context, &mesh, graphics::DrawParam::default()) + .expect("Draw debug mesh"); + } } } diff --git a/examples/ggez/renderer/button.rs b/examples/ggez/renderer/button.rs index fc3ea7ca..486e07ed 100644 --- a/examples/ggez/renderer/button.rs +++ b/examples/ggez/renderer/button.rs @@ -104,6 +104,7 @@ impl button::Renderer for Renderer<'_> { let mut text = Text::new(TextFragment { text: String::from(label), + font: Some(self.font), scale: Some(Scale { x: 20.0, y: 20.0 }), ..Default::default() }); diff --git a/examples/ggez/renderer/debugger.rs b/examples/ggez/renderer/debugger.rs new file mode 100644 index 00000000..98124795 --- /dev/null +++ b/examples/ggez/renderer/debugger.rs @@ -0,0 +1,30 @@ +use super::Renderer; +use ggez::graphics::{Color, DrawMode, MeshBuilder, Rect}; + +impl iced::renderer::Debugger for Renderer<'_> { + type Color = Color; + + fn explain(&mut self, layout: &iced::Layout<'_>, color: Color) { + let bounds = layout.bounds(); + + let mut debug_mesh = + self.debug_mesh.take().unwrap_or(MeshBuilder::new()); + + debug_mesh.rectangle( + DrawMode::stroke(1.0), + Rect { + x: bounds.x, + y: bounds.y, + w: bounds.width, + h: bounds.height, + }, + color, + ); + + self.debug_mesh = Some(debug_mesh); + + for child in layout.children() { + self.explain(&child, color); + } + } +} diff --git a/examples/ggez/renderer/image.rs b/examples/ggez/renderer/image.rs new file mode 100644 index 00000000..c3ead5c9 --- /dev/null +++ b/examples/ggez/renderer/image.rs @@ -0,0 +1,51 @@ +use super::Renderer; + +use ggez::{graphics, nalgebra}; +use iced::image; + +impl image::Renderer<graphics::Image> for Renderer<'_> { + fn node( + &self, + style: iced::Style, + image: &graphics::Image, + width: Option<u16>, + height: Option<u16>, + _source: Option<iced::Rectangle<u16>>, + ) -> iced::Node { + let aspect_ratio = image.width() as f32 / image.height() as f32; + + let style = match (width, height) { + (Some(width), Some(height)) => style.width(width).height(height), + (Some(width), None) => style + .width(width) + .height((width as f32 / aspect_ratio).round() as u16), + (None, Some(height)) => style + .height(height) + .width((height as f32 * aspect_ratio).round() as u16), + (None, None) => style.width(image.width()).height(image.height()), + }; + + iced::Node::new(style) + } + + fn draw( + &mut self, + image: &graphics::Image, + bounds: iced::Rectangle, + _source: Option<iced::Rectangle<u16>>, + ) { + // We should probably use batches to draw images efficiently and keep + // draw side-effect free, but this is good enough for the example. + graphics::draw( + self.context, + image, + graphics::DrawParam::new() + .dest(nalgebra::Point2::new(bounds.x, bounds.y)) + .scale(nalgebra::Vector2::new( + bounds.width / image.width() as f32, + bounds.height / image.height() as f32, + )), + ) + .expect("Draw image"); + } +} diff --git a/examples/ggez/renderer/text.rs b/examples/ggez/renderer/text.rs index a6f782fc..ecf1481e 100644 --- a/examples/ggez/renderer/text.rs +++ b/examples/ggez/renderer/text.rs @@ -6,7 +6,13 @@ use std::cell::RefCell; use std::f32; impl text::Renderer<Color> for Renderer<'_> { - fn node(&self, style: iced::Style, content: &str, size: f32) -> iced::Node { + fn node( + &self, + style: iced::Style, + content: &str, + size: Option<u16>, + ) -> iced::Node { + let font = self.font; let font_cache = graphics::font_cache(self.context); let content = String::from(content); @@ -17,6 +23,7 @@ impl text::Renderer<Color> for Renderer<'_> { // I noticed that the first measure is the one that matters in // practice. Here, we use a RefCell to store the cached measurement. let measure = RefCell::new(None); + let size = size.map(f32::from).unwrap_or(self.font_size); iced::Node::with_measure(style, move |bounds| { let mut measure = measure.borrow_mut(); @@ -35,6 +42,7 @@ impl text::Renderer<Color> for Renderer<'_> { let mut text = Text::new(TextFragment { text: content.clone(), + font: Some(font), scale: Some(Scale { x: size, y: size }), ..Default::default() }); @@ -71,13 +79,16 @@ impl text::Renderer<Color> for Renderer<'_> { &mut self, bounds: iced::Rectangle, content: &str, - size: f32, + size: Option<u16>, color: Option<Color>, horizontal_alignment: text::HorizontalAlignment, _vertical_alignment: text::VerticalAlignment, ) { + let size = size.map(f32::from).unwrap_or(self.font_size); + let mut text = Text::new(TextFragment { text: String::from(content), + font: Some(self.font), scale: Some(Scale { x: size, y: size }), ..Default::default() }); @@ -101,7 +112,7 @@ impl text::Renderer<Color> for Renderer<'_> { x: bounds.x, y: bounds.y, }, - color, + color.or(Some(graphics::BLACK)), ); } } diff --git a/examples/ggez/tour.rs b/examples/ggez/tour.rs index 19982aa4..c2126675 100644 --- a/examples/ggez/tour.rs +++ b/examples/ggez/tour.rs @@ -1,22 +1,26 @@ use super::widget::{ - button, slider, Button, Checkbox, Column, Element, Radio, Row, Slider, Text, + button, slider, Button, Checkbox, Column, Element, Image, Radio, Row, + Slider, Text, }; -use ggez::graphics::{Color, BLACK}; +use ggez::graphics::{self, Color, FilterMode, BLACK}; +use ggez::Context; use iced::{text::HorizontalAlignment, Align}; pub struct Tour { steps: Steps, back_button: button::State, next_button: button::State, + debug: bool, } impl Tour { - pub fn new() -> Tour { + pub fn new(context: &mut Context) -> Tour { Tour { - steps: Steps::new(), + steps: Steps::new(context), back_button: button::State::new(), next_button: button::State::new(), + debug: false, } } @@ -29,7 +33,7 @@ impl Tour { self.steps.advance(); } Message::StepMessage(step_msg) => { - self.steps.update(step_msg); + self.steps.update(step_msg, &mut self.debug); } } } @@ -39,6 +43,7 @@ impl Tour { steps, back_button, next_button, + .. } = self; let mut controls = Row::new(); @@ -59,12 +64,18 @@ impl Tour { ); } - Column::new() + let element: Element<_> = Column::new() .max_width(500) .spacing(20) - .push(steps.layout().map(Message::StepMessage)) + .push(steps.layout(self.debug).map(Message::StepMessage)) .push(controls) - .into() + .into(); + + if self.debug { + element.explain(BLACK) + } else { + element + } } } @@ -81,44 +92,52 @@ struct Steps { } impl Steps { - fn new() -> Steps { + fn new(context: &mut Context) -> Steps { Steps { steps: vec![ Step::Welcome, - Step::Buttons { - primary: button::State::new(), - secondary: button::State::new(), - positive: button::State::new(), - }, - Step::Checkbox { is_checked: false }, - Step::Radio { selection: None }, Step::Slider { state: slider::State::new(), value: 50, }, + Step::RowsAndColumns { + layout: Layout::Row, + spacing_slider: slider::State::new(), + spacing: 20, + }, Step::Text { size_slider: slider::State::new(), size: 30, color_sliders: [slider::State::new(); 3], color: BLACK, }, - Step::RowsAndColumns { - layout: Layout::Row, - spacing_slider: slider::State::new(), - spacing: 20, + Step::Radio { selection: None }, + Step::Image { + ferris: { + let mut image = + graphics::Image::new(context, "/ferris.png") + .expect("Load ferris image"); + + image.set_filter(FilterMode::Linear); + + image + }, + width: 300, + slider: slider::State::new(), }, + Step::Debugger, Step::End, ], current: 0, } } - fn update(&mut self, msg: StepMessage) { - self.steps[self.current].update(msg); + fn update(&mut self, msg: StepMessage, debug: &mut bool) { + self.steps[self.current].update(msg, debug); } - fn layout(&mut self) -> Element<StepMessage> { - self.steps[self.current].layout() + fn layout(&mut self, debug: bool) -> Element<StepMessage> { + self.steps[self.current].layout(debug) } fn advance(&mut self) { @@ -145,52 +164,51 @@ impl Steps { enum Step { Welcome, - Buttons { - primary: button::State, - secondary: button::State, - positive: button::State, - }, - Checkbox { - is_checked: bool, - }, - Radio { - selection: Option<Language>, - }, Slider { state: slider::State, value: u16, }, + RowsAndColumns { + layout: Layout, + spacing_slider: slider::State, + spacing: u16, + }, Text { size_slider: slider::State, size: u16, color_sliders: [slider::State; 3], color: Color, }, - RowsAndColumns { - layout: Layout, - spacing_slider: slider::State, - spacing: u16, + Radio { + selection: Option<Language>, }, + Image { + ferris: graphics::Image, + width: u16, + slider: slider::State, + }, + Debugger, End, } #[derive(Debug, Clone, Copy)] pub enum StepMessage { - CheckboxToggled(bool), - LanguageSelected(Language), SliderChanged(f32), - TextSizeChanged(f32), - TextColorChanged(Color), LayoutChanged(Layout), SpacingChanged(f32), + TextSizeChanged(f32), + TextColorChanged(Color), + LanguageSelected(Language), + ImageWidthChanged(f32), + DebugToggled(bool), } impl<'a> Step { - fn update(&mut self, msg: StepMessage) { + fn update(&mut self, msg: StepMessage, debug: &mut bool) { match msg { - StepMessage::CheckboxToggled(value) => { - if let Step::Checkbox { is_checked } = self { - *is_checked = value; + StepMessage::DebugToggled(value) => { + if let Step::Debugger = self { + *debug = value; } } StepMessage::LanguageSelected(language) => { @@ -223,31 +241,30 @@ impl<'a> Step { *spacing = new_spacing.round() as u16; } } + StepMessage::ImageWidthChanged(new_width) => { + if let Step::Image { width, .. } = self { + *width = new_width.round() as u16; + } + } }; } fn can_continue(&self) -> bool { match self { Step::Welcome => true, - Step::Buttons { .. } => true, - Step::Checkbox { is_checked } => *is_checked, Step::Radio { selection } => *selection == Some(Language::Rust), Step::Slider { .. } => true, Step::Text { .. } => true, + Step::Image { .. } => true, Step::RowsAndColumns { .. } => true, + Step::Debugger => true, Step::End => false, } } - fn layout(&mut self) -> Element<StepMessage> { + fn layout(&mut self, debug: bool) -> Element<StepMessage> { match self { Step::Welcome => Self::welcome().into(), - Step::Buttons { - primary, - secondary, - positive, - } => Self::buttons(primary, secondary, positive).into(), - Step::Checkbox { is_checked } => Self::checkbox(*is_checked).into(), Step::Radio { selection } => Self::radio(*selection).into(), Step::Slider { state, value } => Self::slider(state, *value).into(), Step::Text { @@ -256,6 +273,11 @@ impl<'a> Step { color_sliders, color, } => Self::text(size_slider, *size, color_sliders, *color).into(), + Step::Image { + ferris, + width, + slider, + } => Self::image(ferris.clone(), *width, slider).into(), Step::RowsAndColumns { layout, spacing_slider, @@ -263,6 +285,7 @@ impl<'a> Step { } => { Self::rows_and_columns(*layout, spacing_slider, *spacing).into() } + Step::Debugger => Self::debugger(debug).into(), Step::End => Self::end().into(), } } @@ -277,80 +300,26 @@ impl<'a> Step { fn welcome() -> Column<'a, StepMessage> { Self::container("Welcome!") .push(Text::new( - "This is a tour that introduces some of the features and \ - concepts related with UI development in Iced.", + "This a simple tour meant to showcase a bunch of widgets that \ + can be easily implemented on top of Iced.", )) .push(Text::new( - "You will need to interact with the UI in order to reach the \ - end!", - )) - } - - fn buttons( - primary: &'a mut button::State, - secondary: &'a mut button::State, - positive: &'a mut button::State, - ) -> Column<'a, StepMessage> { - Self::container("Button") - .push(Text::new("A button can fire actions when clicked.")) - .push(Text::new( - "As of now, there are 3 different types of buttons: \ - primary, secondary, and positive.", - )) - .push(Button::new(primary, "Primary")) - .push( - Button::new(secondary, "Secondary") - .class(button::Class::Secondary), - ) - .push( - Button::new(positive, "Positive") - .class(button::Class::Positive), - ) - .push(Text::new( - "Additional types will be added in the near future! Choose \ - each type smartly depending on the situation.", + "Iced is a renderer-agnostic GUI library for Rust focused on \ + simplicity and type-safety. It is heavily inspired by Elm.", )) - } - - fn checkbox(is_checked: bool) -> Column<'a, StepMessage> { - Self::container("Checkbox") .push(Text::new( - "A box that can be checked. Useful to build toggle controls.", - )) - .push(Checkbox::new( - is_checked, - "Show \"Next\" button", - StepMessage::CheckboxToggled, + "It was originally born as part of Coffee, an opinionated \ + 2D game engine for Rust.", )) .push(Text::new( - "A checkbox always has a label, and both the checkbox and its \ - label can be clicked to toggle it.", + "Iced does not provide a built-in renderer. This example runs \ + on a fairly simple renderer built on top of ggez, another \ + game library.", )) - } - - fn radio(selection: Option<Language>) -> Column<'a, StepMessage> { - let question = Column::new() - .padding(20) - .spacing(10) - .push(Text::new("Iced is written in...")) - .push(Language::all().iter().cloned().fold( - Column::new().padding(10).spacing(20), - |choices, language| { - choices.push(Radio::new( - language, - language.into(), - selection, - StepMessage::LanguageSelected, - )) - }, - )); - - Self::container("Radio button") .push(Text::new( - "A radio button is normally used to represent a choice. Like \ - a checkbox, it always has a label.", + "You will need to interact with the UI in order to reach the \ + end!", )) - .push(question) } fn slider( @@ -378,55 +347,6 @@ impl<'a> Step { ) } - fn text( - size_slider: &'a mut slider::State, - size: u16, - color_sliders: &'a mut [slider::State; 3], - color: Color, - ) -> Column<'a, StepMessage> { - let size_section = Column::new() - .padding(20) - .spacing(20) - .push(Text::new("You can change its size:")) - .push( - Text::new(&format!("This text is {} points", size)).size(size), - ) - .push(Slider::new( - size_slider, - 10.0..=50.0, - size as f32, - StepMessage::TextSizeChanged, - )); - - let [red, green, blue] = color_sliders; - let color_section = Column::new() - .padding(20) - .spacing(20) - .push(Text::new("And its color:")) - .push(Text::new(&format!("{:?}", color)).color(color)) - .push( - Row::new() - .spacing(10) - .push(Slider::new(red, 0.0..=1.0, color.r, move |r| { - StepMessage::TextColorChanged(Color { r, ..color }) - })) - .push(Slider::new(green, 0.0..=1.0, color.g, move |g| { - StepMessage::TextColorChanged(Color { g, ..color }) - })) - .push(Slider::new(blue, 0.0..=1.0, color.b, move |b| { - StepMessage::TextColorChanged(Color { b, ..color }) - })), - ); - - Self::container("Text") - .push(Text::new( - "Text is probably the most essential widget for your UI. \ - It will try to adapt to the dimensions of its container.", - )) - .push(size_section) - .push(color_section) - } - fn rows_and_columns( layout: Layout, spacing_slider: &'a mut slider::State, @@ -463,7 +383,7 @@ impl<'a> Step { .spacing(10) .push(Slider::new( spacing_slider, - 0.0..=100.0, + 0.0..=80.0, spacing as f32, StepMessage::SpacingChanged, )) @@ -489,10 +409,127 @@ impl<'a> Step { .push(spacing_section) } + fn text( + size_slider: &'a mut slider::State, + size: u16, + color_sliders: &'a mut [slider::State; 3], + color: Color, + ) -> Column<'a, StepMessage> { + let size_section = Column::new() + .padding(20) + .spacing(20) + .push(Text::new("You can change its size:")) + .push( + Text::new(&format!("This text is {} pixels", size)).size(size), + ) + .push(Slider::new( + size_slider, + 10.0..=70.0, + size as f32, + StepMessage::TextSizeChanged, + )); + + let [red, green, blue] = color_sliders; + let color_section = Column::new() + .padding(20) + .spacing(20) + .push(Text::new("And its color:")) + .push(Text::new(&format!("{:?}", color)).color(color)) + .push( + Row::new() + .spacing(10) + .push(Slider::new(red, 0.0..=1.0, color.r, move |r| { + StepMessage::TextColorChanged(Color { r, ..color }) + })) + .push(Slider::new(green, 0.0..=1.0, color.g, move |g| { + StepMessage::TextColorChanged(Color { g, ..color }) + })) + .push(Slider::new(blue, 0.0..=1.0, color.b, move |b| { + StepMessage::TextColorChanged(Color { b, ..color }) + })), + ); + + Self::container("Text") + .push(Text::new( + "Text is probably the most essential widget for your UI. \ + It will try to adapt to the dimensions of its container.", + )) + .push(size_section) + .push(color_section) + } + + fn radio(selection: Option<Language>) -> Column<'a, StepMessage> { + let question = Column::new() + .padding(20) + .spacing(10) + .push(Text::new("Iced is written in...").size(24)) + .push(Language::all().iter().cloned().fold( + Column::new().padding(10).spacing(20), + |choices, language| { + choices.push(Radio::new( + language, + language.into(), + selection, + StepMessage::LanguageSelected, + )) + }, + )); + + Self::container("Radio button") + .push(Text::new( + "A radio button is normally used to represent a choice... \ + Surprise test!", + )) + .push(question) + .push(Text::new( + "Iced works very well with iterators! The list above is \ + basically created by folding a column over the different \ + choices, creating a radio button for each one of them!", + )) + } + + fn image( + ferris: graphics::Image, + width: u16, + slider: &'a mut slider::State, + ) -> Column<'a, StepMessage> { + Self::container("Image") + .push(Text::new("An image that tries to keep its aspect ratio.")) + .push(Image::new(ferris).width(width).align_self(Align::Center)) + .push(Slider::new( + slider, + 100.0..=500.0, + width as f32, + StepMessage::ImageWidthChanged, + )) + .push( + Text::new(&format!("Width: {} px", width.to_string())) + .horizontal_alignment(HorizontalAlignment::Center), + ) + } + + fn debugger(debug: bool) -> Column<'a, StepMessage> { + Self::container("Debugger") + .push(Text::new( + "You can ask Iced to visually explain the layouting of the \ + different elements comprising your UI!", + )) + .push(Text::new( + "Give it a shot! Check the following checkbox to be able to \ + see element boundaries.", + )) + .push(Checkbox::new( + debug, + "Explain layout", + StepMessage::DebugToggled, + )) + .push(Text::new("Feel free to go back and take a look.")) + } + fn end() -> Column<'a, StepMessage> { Self::container("You reached the end!") .push(Text::new( - "This tour will be extended as more features are added.", + "This tour will be updated as more features are added.", )) .push(Text::new("Make sure to keep an eye on it!")) } diff --git a/examples/ggez/widget.rs b/examples/ggez/widget.rs index a5fcccc6..9a141c83 100644 --- a/examples/ggez/widget.rs +++ b/examples/ggez/widget.rs @@ -1,12 +1,13 @@ use super::Renderer; -use ggez::graphics::Color; +use ggez::graphics::{self, Color}; pub use iced::{button, slider, Button, Slider}; pub type Text = iced::Text<Color>; pub type Checkbox<Message> = iced::Checkbox<Color, Message>; pub type Radio<Message> = iced::Radio<Color, Message>; +pub type Image = iced::Image<graphics::Image>; pub type Column<'a, Message> = iced::Column<'a, Message, Renderer<'a>>; pub type Row<'a, Message> = iced::Row<'a, Message, Renderer<'a>>; diff --git a/examples/resources/Roboto-LICENSE b/examples/resources/Roboto-LICENSE new file mode 100644 index 00000000..75b52484 --- /dev/null +++ b/examples/resources/Roboto-LICENSE @@ -0,0 +1,202 @@ +
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/examples/resources/Roboto-Regular.ttf b/examples/resources/Roboto-Regular.ttf Binary files differnew file mode 100644 index 00000000..2b6392ff --- /dev/null +++ b/examples/resources/Roboto-Regular.ttf diff --git a/examples/resources/ferris.png b/examples/resources/ferris.png Binary files differnew file mode 100644 index 00000000..ebce1a14 --- /dev/null +++ b/examples/resources/ferris.png @@ -97,7 +97,7 @@ //! # } //! # //! # impl text::Renderer<[f32; 4]> for Renderer { -//! # fn node(&self, style: Style, _content: &str, _size: f32) -> Node { +//! # fn node(&self, style: Style, _content: &str, _size: Option<u16>) -> Node { //! # Node::new(style) //! # } //! # @@ -105,7 +105,7 @@ //! # &mut self, //! # _bounds: Rectangle, //! # _content: &str, -//! # _size: f32, +//! # _size: Option<u16>, //! # _color: Option<[f32; 4]>, //! # _horizontal_alignment: HorizontalAlignment, //! # _vertical_alignment: VerticalAlignment, diff --git a/src/style.rs b/src/style.rs index 9e1f7df2..575ea366 100644 --- a/src/style.rs +++ b/src/style.rs @@ -11,7 +11,7 @@ impl Style { /// Defines the width of a [`Node`] in pixels. /// /// [`Node`]: struct.Node.html - pub fn width(mut self, width: u32) -> Self { + pub fn width(mut self, width: u16) -> Self { self.0.size.width = style::Dimension::Points(width as f32); self } @@ -19,7 +19,7 @@ impl Style { /// Defines the height of a [`Node`] in pixels. /// /// [`Node`]: struct.Node.html - pub fn height(mut self, height: u32) -> Self { + pub fn height(mut self, height: u16) -> Self { self.0.size.height = style::Dimension::Points(height as f32); self } @@ -27,7 +27,7 @@ impl Style { /// Defines the minimum width of a [`Node`] in pixels. /// /// [`Node`]: struct.Node.html - pub fn min_width(mut self, min_width: u32) -> Self { + pub fn min_width(mut self, min_width: u16) -> Self { self.0.min_size.width = style::Dimension::Points(min_width as f32); self } @@ -35,7 +35,7 @@ impl Style { /// Defines the maximum width of a [`Node`] in pixels. /// /// [`Node`]: struct.Node.html - pub fn max_width(mut self, max_width: u32) -> Self { + pub fn max_width(mut self, max_width: u16) -> Self { self.0.max_size.width = style::Dimension::Points(max_width as f32); self.fill_width() } @@ -43,16 +43,18 @@ impl Style { /// Defines the minimum height of a [`Node`] in pixels. /// /// [`Node`]: struct.Node.html - pub fn min_height(mut self, min_height: u32) -> Self { - self.0.min_size.height = style::Dimension::Points(min_height as f32); + pub fn min_height(mut self, min_height: u16) -> Self { + self.0.min_size.height = + style::Dimension::Points(f32::from(min_height)); self } /// Defines the maximum height of a [`Node`] in pixels. /// /// [`Node`]: struct.Node.html - pub fn max_height(mut self, max_height: u32) -> Self { - self.0.max_size.height = style::Dimension::Points(max_height as f32); + pub fn max_height(mut self, max_height: u16) -> Self { + self.0.max_size.height = + style::Dimension::Points(f32::from(max_height)); self.fill_height() } @@ -100,7 +102,7 @@ impl Style { /// Sets the padding of a [`Node`] in pixels. /// /// [`Node`]: struct.Node.html - pub fn padding(mut self, px: u32) -> Self { + pub fn padding(mut self, px: u16) -> Self { self.0.padding = stretch::geometry::Rect { start: style::Dimension::Points(px as f32), end: style::Dimension::Points(px as f32), diff --git a/src/widget.rs b/src/widget.rs index b8ecb409..30606934 100644 --- a/src/widget.rs +++ b/src/widget.rs @@ -26,16 +26,16 @@ mod row; pub mod button; pub mod checkbox; pub mod image; -pub mod progress_bar; +//pub mod progress_bar; pub mod radio; pub mod slider; pub mod text; pub use button::Button; pub use checkbox::Checkbox; -pub use image::Image; pub use column::Column; -pub use progress_bar::ProgressBar; +pub use image::Image; +//pub use progress_bar::ProgressBar; pub use radio::Radio; pub use row::Row; pub use slider::Slider; diff --git a/src/widget/button.rs b/src/widget/button.rs index 14fd3852..abcdbfeb 100644 --- a/src/widget/button.rs +++ b/src/widget/button.rs @@ -83,7 +83,7 @@ impl<'a, Message> Button<'a, Message> { /// Sets the width of the [`Button`] in pixels. /// /// [`Button`]: struct.Button.html - pub fn width(mut self, width: u32) -> Self { + pub fn width(mut self, width: u16) -> Self { self.style = self.style.width(width); self } diff --git a/src/widget/checkbox.rs b/src/widget/checkbox.rs index 34d5df53..c60807fd 100644 --- a/src/widget/checkbox.rs +++ b/src/widget/checkbox.rs @@ -142,7 +142,7 @@ where renderer, text_bounds, &self.label, - 20.0, + None, self.label_color, text::HorizontalAlignment::Left, text::VerticalAlignment::Top, diff --git a/src/widget/column.rs b/src/widget/column.rs index 903de897..ff754e98 100644 --- a/src/widget/column.rs +++ b/src/widget/column.rs @@ -55,7 +55,7 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> { /// Sets the padding of the [`Column`] in pixels. /// /// [`Column`]: struct.Column.html - pub fn padding(mut self, px: u32) -> Self { + pub fn padding(mut self, px: u16) -> Self { self.style = self.style.padding(px); self } @@ -63,7 +63,7 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> { /// Sets the width of the [`Column`] in pixels. /// /// [`Column`]: struct.Column.html - pub fn width(mut self, width: u32) -> Self { + pub fn width(mut self, width: u16) -> Self { self.style = self.style.width(width); self } @@ -71,7 +71,7 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> { /// Sets the height of the [`Column`] in pixels. /// /// [`Column`]: struct.Column.html - pub fn height(mut self, height: u32) -> Self { + pub fn height(mut self, height: u16) -> Self { self.style = self.style.height(height); self } @@ -79,7 +79,7 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> { /// Sets the maximum width of the [`Column`] in pixels. /// /// [`Column`]: struct.Column.html - pub fn max_width(mut self, max_width: u32) -> Self { + pub fn max_width(mut self, max_width: u16) -> Self { self.style = self.style.max_width(max_width); self } @@ -87,7 +87,7 @@ impl<'a, Message, Renderer> Column<'a, Message, Renderer> { /// Sets the maximum height of the [`Column`] in pixels. /// /// [`Column`]: struct.Column.html - pub fn max_height(mut self, max_height: u32) -> Self { + pub fn max_height(mut self, max_height: u16) -> Self { self.style = self.style.max_height(max_height); self } diff --git a/src/widget/image.rs b/src/widget/image.rs index 8cbcd02c..161dc963 100644 --- a/src/widget/image.rs +++ b/src/widget/image.rs @@ -1,7 +1,8 @@ //! Display images in your user interface. use crate::{ - Element, Hasher, Layout, MouseCursor, Node, Point, Rectangle, Style, Widget, + Align, Element, Hasher, Layout, MouseCursor, Node, Point, Rectangle, Style, + Widget, }; use std::hash::Hash; @@ -25,6 +26,8 @@ use std::hash::Hash; pub struct Image<I> { image: I, source: Option<Rectangle<u16>>, + width: Option<u16>, + height: Option<u16>, style: Style, } @@ -32,6 +35,8 @@ impl<I> std::fmt::Debug for Image<I> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("Image") .field("source", &self.source) + .field("width", &self.width) + .field("height", &self.height) .field("style", &self.style) .finish() } @@ -45,7 +50,9 @@ impl<I> Image<I> { Image { image, source: None, - style: Style::default().fill_width().fill_height(), + width: None, + height: None, + style: Style::default(), } } @@ -60,16 +67,27 @@ impl<I> Image<I> { /// Sets the width of the [`Image`] boundaries in pixels. /// /// [`Image`]: struct.Image.html - pub fn width(mut self, width: u32) -> Self { - self.style = self.style.width(width); + pub fn width(mut self, width: u16) -> Self { + self.width = Some(width); self } /// Sets the height of the [`Image`] boundaries in pixels. /// /// [`Image`]: struct.Image.html - pub fn height(mut self, height: u32) -> Self { - self.style = self.style.height(height); + pub fn height(mut self, height: u16) -> Self { + self.height = Some(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.Button.html + pub fn align_self(mut self, align: Align) -> Self { + self.style = self.style.align_self(align); self } } @@ -79,8 +97,14 @@ where Renderer: self::Renderer<I>, I: Clone, { - fn node(&self, _renderer: &Renderer) -> Node { - Node::new(self.style) + fn node(&self, renderer: &Renderer) -> Node { + renderer.node( + self.style, + &self.image, + self.width, + self.height, + self.source, + ) } fn draw( @@ -89,13 +113,15 @@ where layout: Layout<'_>, _cursor_position: Point, ) -> MouseCursor { - renderer.draw(layout.bounds(), self.image.clone(), self.source); + renderer.draw(&self.image, layout.bounds(), self.source); MouseCursor::OutOfBounds } fn hash_layout(&self, state: &mut Hasher) { self.style.hash(state); + self.width.hash(state); + self.height.hash(state); } } @@ -107,6 +133,23 @@ where /// [`Image`]: struct.Image.html /// [renderer]: ../../renderer/index.html pub trait Renderer<I> { + /// Creates a [`Node`] with the given [`Style`] for the provided [`Image`] + /// and its size. + /// + /// You should probably keep the original aspect ratio, if possible. + /// + /// [`Node`]: ../../struct.Node.html + /// [`Style`]: ../../struct.Style.html + /// [`Image`]: struct.Image.html + fn node( + &self, + style: Style, + image: &I, + width: Option<u16>, + height: Option<u16>, + source: Option<Rectangle<u16>>, + ) -> Node; + /// Draws an [`Image`]. /// /// It receives: @@ -118,8 +161,8 @@ pub trait Renderer<I> { /// [`Image`]: struct.Image.html fn draw( &mut self, + image: &I, bounds: Rectangle<f32>, - image: I, source: Option<Rectangle<u16>>, ); } diff --git a/src/widget/progress_bar.rs b/src/widget/progress_bar.rs index 645c7277..d4499160 100644 --- a/src/widget/progress_bar.rs +++ b/src/widget/progress_bar.rs @@ -47,7 +47,7 @@ impl ProgressBar { /// Sets the width of the [`ProgressBar`] in pixels. /// /// [`ProgressBar`]: struct.ProgressBar.html - pub fn width(mut self, width: u32) -> Self { + pub fn width(mut self, width: u16) -> Self { self.style = self.style.width(width); self } diff --git a/src/widget/radio.rs b/src/widget/radio.rs index a59d52aa..28353ef4 100644 --- a/src/widget/radio.rs +++ b/src/widget/radio.rs @@ -152,7 +152,7 @@ where renderer, text_bounds, &self.label, - 20.0, + None, self.label_color, text::HorizontalAlignment::Left, text::VerticalAlignment::Top, diff --git a/src/widget/row.rs b/src/widget/row.rs index 7b7033a1..959528dc 100644 --- a/src/widget/row.rs +++ b/src/widget/row.rs @@ -52,7 +52,7 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> { /// Sets the padding of the [`Row`] in pixels. /// /// [`Row`]: struct.Row.html - pub fn padding(mut self, px: u32) -> Self { + pub fn padding(mut self, px: u16) -> Self { self.style = self.style.padding(px); self } @@ -60,7 +60,7 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> { /// Sets the width of the [`Row`] in pixels. /// /// [`Row`]: struct.Row.html - pub fn width(mut self, width: u32) -> Self { + pub fn width(mut self, width: u16) -> Self { self.style = self.style.width(width); self } @@ -68,7 +68,7 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> { /// Sets the height of the [`Row`] in pixels. /// /// [`Row`]: struct.Row.html - pub fn height(mut self, height: u32) -> Self { + pub fn height(mut self, height: u16) -> Self { self.style = self.style.height(height); self } @@ -76,7 +76,7 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> { /// Sets the maximum width of the [`Row`] in pixels. /// /// [`Row`]: struct.Row.html - pub fn max_width(mut self, max_width: u32) -> Self { + pub fn max_width(mut self, max_width: u16) -> Self { self.style = self.style.max_width(max_width); self } @@ -84,7 +84,7 @@ impl<'a, Message, Renderer> Row<'a, Message, Renderer> { /// Sets the maximum height of the [`Row`] in pixels. /// /// [`Row`]: struct.Row.html - pub fn max_height(mut self, max_height: u32) -> Self { + pub fn max_height(mut self, max_height: u16) -> Self { self.style = self.style.max_height(max_height); self } diff --git a/src/widget/slider.rs b/src/widget/slider.rs index c7adbb51..cdec9ec4 100644 --- a/src/widget/slider.rs +++ b/src/widget/slider.rs @@ -93,7 +93,7 @@ impl<'a, Message> Slider<'a, Message> { /// Sets the width of the [`Slider`] in pixels. /// /// [`Slider`]: struct.Slider.html - pub fn width(mut self, width: u32) -> Self { + pub fn width(mut self, width: u16) -> Self { self.style = self.style.width(width); self } diff --git a/src/widget/text.rs b/src/widget/text.rs index 7b62e5cb..59b599bb 100644 --- a/src/widget/text.rs +++ b/src/widget/text.rs @@ -30,7 +30,7 @@ use std::hash::Hash; #[derive(Debug, Clone)] pub struct Text<Color> { content: String, - size: u16, + size: Option<u16>, color: Option<Color>, style: Style, horizontal_alignment: HorizontalAlignment, @@ -44,7 +44,7 @@ impl<Color> Text<Color> { pub fn new(label: &str) -> Self { Text { content: String::from(label), - size: 20, + size: None, color: None, style: Style::default().fill_width(), horizontal_alignment: HorizontalAlignment::Left, @@ -56,7 +56,7 @@ impl<Color> Text<Color> { /// /// [`Text`]: struct.Text.html pub fn size(mut self, size: u16) -> Self { - self.size = size; + self.size = Some(size); self } @@ -71,7 +71,7 @@ impl<Color> Text<Color> { /// Sets the width of the [`Text`] boundaries in pixels. /// /// [`Text`]: struct.Text.html - pub fn width(mut self, width: u32) -> Self { + pub fn width(mut self, width: u16) -> Self { self.style = self.style.width(width); self } @@ -79,7 +79,7 @@ impl<Color> Text<Color> { /// Sets the height of the [`Text`] boundaries in pixels. /// /// [`Text`]: struct.Text.html - pub fn height(mut self, height: u32) -> Self { + pub fn height(mut self, height: u16) -> Self { self.style = self.style.height(height); self } @@ -112,7 +112,7 @@ where Renderer: self::Renderer<Color>, { fn node(&self, renderer: &Renderer) -> Node { - renderer.node(self.style, &self.content, f32::from(self.size)) + renderer.node(self.style, &self.content, self.size) } fn draw( @@ -124,7 +124,7 @@ where renderer.draw( layout.bounds(), &self.content, - f32::from(self.size), + self.size, self.color, self.horizontal_alignment, self.vertical_alignment, @@ -160,7 +160,7 @@ pub trait Renderer<Color> { /// [`Style`]: ../../struct.Style.html /// [`Text`]: struct.Text.html /// [`Node::with_measure`]: ../../struct.Node.html#method.with_measure - fn node(&self, style: Style, content: &str, size: f32) -> Node; + fn node(&self, style: Style, content: &str, size: Option<u16>) -> Node; /// Draws a [`Text`] fragment. /// @@ -179,7 +179,7 @@ pub trait Renderer<Color> { &mut self, bounds: Rectangle, content: &str, - size: f32, + size: Option<u16>, color: Option<Color>, horizontal_alignment: HorizontalAlignment, vertical_alignment: VerticalAlignment, |