Improve tour example

This commit is contained in:
Héctor Ramón Jiménez 2019-09-04 11:09:57 +02:00
parent 2c35103035
commit c583a2174d
24 changed files with 644 additions and 239 deletions

View file

@ -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,

View file

@ -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");
}
}
}

View file

@ -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()
});

View file

@ -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);
}
}
}

View file

@ -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");
}
}

View file

@ -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)),
);
}
}

View file

@ -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,8 +300,21 @@ 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(
"Iced is a renderer-agnostic GUI library for Rust focused on \
simplicity and type-safety. It is heavily inspired by Elm.",
))
.push(Text::new(
"It was originally born as part of Coffee, an opinionated \
2D game engine for Rust.",
))
.push(Text::new(
"Iced does not provide a built-in renderer. This example runs \
on a fairly simple renderer built on top of ggez, another \
game library.",
))
.push(Text::new(
"You will need to interact with the UI in order to reach the \
@ -286,73 +322,6 @@ impl<'a> Step {
))
}
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.",
))
}
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,
))
.push(Text::new(
"A checkbox always has a label, and both the checkbox and its \
label can be clicked to toggle it.",
))
}
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.",
))
.push(question)
}
fn slider(
state: &'a mut slider::State,
value: u16,
@ -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!"))
}

View file

@ -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>>;

View file

@ -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.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB