Compare commits
52 Commits
feature/dy
...
master
Author | SHA1 | Date |
---|---|---|
Olivier 'reivilibre' | 2dc42b4a22 | |
Olivier 'reivilibre' | 343e6332d7 | |
Olivier 'reivilibre' | 3a5f866782 | |
Olivier 'reivilibre' | e4b626b91f | |
Olivier 'reivilibre' | 45ecf02504 | |
Héctor Ramón Jiménez | 1b6cf05f5f | |
Héctor Ramón | c6c3594c83 | |
Héctor Ramón | 06d0158efb | |
Richard | 612585109f | |
Richard | 96a462d2f2 | |
Richard | 9ae22b58d8 | |
Héctor Ramón | d2c8a3e04b | |
Imbris | ba51661a2a | |
Diego Fujii | 80df17ab55 | |
Héctor Ramón | b62fcca9b9 | |
Héctor Ramón | 94ee2566c4 | |
Héctor Ramón | bb6e06127e | |
Héctor Ramón Jiménez | 15c17a7250 | |
Poly | a53e7559fe | |
Poly | c70f90f320 | |
Clark Moody | 27b42ca6b6 | |
Yusuf Bera Ertan | 83d19689c8 | |
Héctor Ramón | e68da229b3 | |
Héctor Ramón | d1797dda87 | |
Héctor Ramón | d46dd67a91 | |
Jonas Matser | e66120b9c1 | |
Jonas Matser | dbc1181011 | |
Héctor Ramón | f6ff87bb8f | |
Héctor Ramón | 56f673d819 | |
Héctor Ramón | 5224cc7f26 | |
nlevy | 656dc357f8 | |
Héctor Ramón | 0e70b11e00 | |
Héctor Ramón | ce3a5f19b9 | |
Héctor Ramón | 3051d4ec76 | |
Héctor Ramón | 57510c43c8 | |
Héctor Ramón | 827577c179 | |
Yusuf Bera Ertan | f7d6e40bf0 | |
Héctor Ramón | 397a5c06ec | |
Héctor Ramón | d3d6f3efb3 | |
Héctor Ramón | ef5f46bcdd | |
Héctor Ramón | a32ce271bd | |
Kaiden42 | 2a5aa69024 | |
Kaiden42 | e00fca6372 | |
Kaiden42 | c0cfd9d5cf | |
Kaiden42 | be3ee9adf1 | |
Kaiden42 | 1ef38cc207 | |
Kaiden42 | 7a626f3b7b | |
Kaiden42 | aa18a6e0d5 | |
Kaiden42 | bab71971fb | |
Kaiden42 | 88da268724 | |
Kaiden42 | 7370dfac6e | |
Kaiden42 | 52a185fbab |
|
@ -11,6 +11,11 @@ jobs:
|
|||
- name: Install cargo-deb
|
||||
run: cargo install cargo-deb
|
||||
- uses: actions/checkout@master
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
export DEBIAN_FRONTED=noninteractive
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get install -y libxkbcommon-dev
|
||||
- name: Enable Link Time Optimizations
|
||||
run: |
|
||||
echo "[profile.release]" >> Cargo.toml
|
||||
|
|
|
@ -12,6 +12,12 @@ jobs:
|
|||
with:
|
||||
rust-version: ${{ matrix.rust }}
|
||||
- uses: actions/checkout@master
|
||||
- name: Install dependencies
|
||||
if: matrix.os == 'ubuntu-latest'
|
||||
run: |
|
||||
export DEBIAN_FRONTED=noninteractive
|
||||
sudo apt-get -qq update
|
||||
sudo apt-get install -y libxkbcommon-dev
|
||||
- name: Run tests
|
||||
run: |
|
||||
cargo test --verbose --all
|
||||
|
|
|
@ -83,7 +83,9 @@ members = [
|
|||
"examples/svg",
|
||||
"examples/todos",
|
||||
"examples/tour",
|
||||
"examples/tour_glow",
|
||||
"examples/tooltip",
|
||||
"examples/url_handler",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
|
|
|
@ -105,8 +105,8 @@ impl Rectangle<f32> {
|
|||
Rectangle {
|
||||
x: self.x as u32,
|
||||
y: self.y as u32,
|
||||
width: self.width.ceil() as u32,
|
||||
height: self.height.ceil() as u32,
|
||||
width: self.width as u32,
|
||||
height: self.height as u32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
mod style;
|
||||
|
||||
use iced::{
|
||||
button, scrollable, Button, Column, Container, Element, Length, Radio, Row,
|
||||
Rule, Sandbox, Scrollable, Settings, Space, Text,
|
||||
button, scrollable, Button, Column, Container, Element, Length,
|
||||
ProgressBar, Radio, Row, Rule, Sandbox, Scrollable, Settings, Space, Text,
|
||||
};
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
|
@ -17,6 +17,9 @@ struct ScrollableDemo {
|
|||
#[derive(Debug, Clone)]
|
||||
enum Message {
|
||||
ThemeChanged(style::Theme),
|
||||
ScrollToTop(usize),
|
||||
ScrollToBottom(usize),
|
||||
Scrolled(usize, f32),
|
||||
}
|
||||
|
||||
impl Sandbox for ScrollableDemo {
|
||||
|
@ -36,6 +39,25 @@ impl Sandbox for ScrollableDemo {
|
|||
fn update(&mut self, message: Message) {
|
||||
match message {
|
||||
Message::ThemeChanged(theme) => self.theme = theme,
|
||||
Message::ScrollToTop(i) => {
|
||||
if let Some(variant) = self.variants.get_mut(i) {
|
||||
variant.scrollable.snap_to(0.0);
|
||||
|
||||
variant.latest_offset = 0.0;
|
||||
}
|
||||
}
|
||||
Message::ScrollToBottom(i) => {
|
||||
if let Some(variant) = self.variants.get_mut(i) {
|
||||
variant.scrollable.snap_to(1.0);
|
||||
|
||||
variant.latest_offset = 1.0;
|
||||
}
|
||||
}
|
||||
Message::Scrolled(i, offset) => {
|
||||
if let Some(variant) = self.variants.get_mut(i) {
|
||||
variant.latest_offset = offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,15 +84,28 @@ impl Sandbox for ScrollableDemo {
|
|||
let scrollable_row = Row::with_children(
|
||||
variants
|
||||
.iter_mut()
|
||||
.map(|variant| {
|
||||
.enumerate()
|
||||
.map(|(i, variant)| {
|
||||
let mut scrollable =
|
||||
Scrollable::new(&mut variant.scrollable)
|
||||
.padding(10)
|
||||
.spacing(10)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.on_scroll(move |offset| {
|
||||
Message::Scrolled(i, offset)
|
||||
})
|
||||
.style(*theme)
|
||||
.push(Text::new(variant.title));
|
||||
.push(Text::new(variant.title))
|
||||
.push(
|
||||
Button::new(
|
||||
&mut variant.scroll_to_bottom,
|
||||
Text::new("Scroll to bottom"),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.padding(10)
|
||||
.on_press(Message::ScrollToBottom(i)),
|
||||
);
|
||||
|
||||
if let Some(scrollbar_width) = variant.scrollbar_width {
|
||||
scrollable = scrollable
|
||||
|
@ -110,20 +145,31 @@ impl Sandbox for ScrollableDemo {
|
|||
.push(Space::with_height(Length::Units(1200)))
|
||||
.push(Text::new("Middle"))
|
||||
.push(Space::with_height(Length::Units(1200)))
|
||||
.push(Text::new("The End."))
|
||||
.push(
|
||||
Button::new(
|
||||
&mut variant.button,
|
||||
Text::new("I am a button"),
|
||||
&mut variant.scroll_to_top,
|
||||
Text::new("Scroll to top"),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.padding(10),
|
||||
)
|
||||
.push(Text::new("The End."));
|
||||
.padding(10)
|
||||
.on_press(Message::ScrollToTop(i)),
|
||||
);
|
||||
|
||||
Container::new(scrollable)
|
||||
Column::new()
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.style(*theme)
|
||||
.spacing(10)
|
||||
.push(
|
||||
Container::new(scrollable)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.style(*theme),
|
||||
)
|
||||
.push(ProgressBar::new(
|
||||
0.0..=1.0,
|
||||
variant.latest_offset,
|
||||
))
|
||||
.into()
|
||||
})
|
||||
.collect(),
|
||||
|
@ -153,10 +199,12 @@ impl Sandbox for ScrollableDemo {
|
|||
struct Variant {
|
||||
title: &'static str,
|
||||
scrollable: scrollable::State,
|
||||
button: button::State,
|
||||
scroll_to_top: button::State,
|
||||
scroll_to_bottom: button::State,
|
||||
scrollbar_width: Option<u16>,
|
||||
scrollbar_margin: Option<u16>,
|
||||
scroller_width: Option<u16>,
|
||||
latest_offset: f32,
|
||||
}
|
||||
|
||||
impl Variant {
|
||||
|
@ -165,34 +213,42 @@ impl Variant {
|
|||
Self {
|
||||
title: "Default Scrollbar",
|
||||
scrollable: scrollable::State::new(),
|
||||
button: button::State::new(),
|
||||
scroll_to_top: button::State::new(),
|
||||
scroll_to_bottom: button::State::new(),
|
||||
scrollbar_width: None,
|
||||
scrollbar_margin: None,
|
||||
scroller_width: None,
|
||||
latest_offset: 0.0,
|
||||
},
|
||||
Self {
|
||||
title: "Slimmed & Margin",
|
||||
scrollable: scrollable::State::new(),
|
||||
button: button::State::new(),
|
||||
scroll_to_top: button::State::new(),
|
||||
scroll_to_bottom: button::State::new(),
|
||||
scrollbar_width: Some(4),
|
||||
scrollbar_margin: Some(3),
|
||||
scroller_width: Some(4),
|
||||
latest_offset: 0.0,
|
||||
},
|
||||
Self {
|
||||
title: "Wide Scroller",
|
||||
scrollable: scrollable::State::new(),
|
||||
button: button::State::new(),
|
||||
scroll_to_top: button::State::new(),
|
||||
scroll_to_bottom: button::State::new(),
|
||||
scrollbar_width: Some(4),
|
||||
scrollbar_margin: None,
|
||||
scroller_width: Some(10),
|
||||
latest_offset: 0.0,
|
||||
},
|
||||
Self {
|
||||
title: "Narrow Scroller",
|
||||
scrollable: scrollable::State::new(),
|
||||
button: button::State::new(),
|
||||
scroll_to_top: button::State::new(),
|
||||
scroll_to_bottom: button::State::new(),
|
||||
scrollbar_width: Some(10),
|
||||
scrollbar_margin: None,
|
||||
scroller_width: Some(4),
|
||||
latest_offset: 0.0,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use iced::{
|
||||
button, scrollable, slider, text_input, Align, Button, Checkbox, Column,
|
||||
Container, Element, Length, ProgressBar, Radio, Row, Rule, Sandbox,
|
||||
Scrollable, Settings, Slider, Space, Text, TextInput,
|
||||
Scrollable, Settings, Slider, Space, Text, TextInput, Toggler,
|
||||
};
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
|
@ -17,7 +17,8 @@ struct Styling {
|
|||
button: button::State,
|
||||
slider: slider::State,
|
||||
slider_value: f32,
|
||||
toggle_value: bool,
|
||||
checkbox_value: bool,
|
||||
toggler_value: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
@ -27,6 +28,7 @@ enum Message {
|
|||
ButtonPressed,
|
||||
SliderChanged(f32),
|
||||
CheckboxToggled(bool),
|
||||
TogglerToggled(bool),
|
||||
}
|
||||
|
||||
impl Sandbox for Styling {
|
||||
|
@ -46,7 +48,8 @@ impl Sandbox for Styling {
|
|||
Message::InputChanged(value) => self.input_value = value,
|
||||
Message::ButtonPressed => {}
|
||||
Message::SliderChanged(value) => self.slider_value = value,
|
||||
Message::CheckboxToggled(value) => self.toggle_value = value,
|
||||
Message::CheckboxToggled(value) => self.checkbox_value = value,
|
||||
Message::TogglerToggled(value) => self.toggler_value = value,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -101,11 +104,19 @@ impl Sandbox for Styling {
|
|||
.push(Text::new("You did it!"));
|
||||
|
||||
let checkbox = Checkbox::new(
|
||||
self.toggle_value,
|
||||
"Toggle me!",
|
||||
self.checkbox_value,
|
||||
"Check me!",
|
||||
Message::CheckboxToggled,
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.style(self.theme);
|
||||
|
||||
let toggler = Toggler::new(
|
||||
self.toggler_value,
|
||||
String::from("Toggle me!"),
|
||||
Message::TogglerToggled,
|
||||
)
|
||||
.width(Length::Shrink)
|
||||
.spacing(10)
|
||||
.style(self.theme);
|
||||
|
||||
let content = Column::new()
|
||||
|
@ -124,7 +135,13 @@ impl Sandbox for Styling {
|
|||
.align_items(Align::Center)
|
||||
.push(scrollable)
|
||||
.push(Rule::vertical(38).style(self.theme))
|
||||
.push(checkbox),
|
||||
.push(
|
||||
Column::new()
|
||||
.width(Length::Shrink)
|
||||
.spacing(20)
|
||||
.push(checkbox)
|
||||
.push(toggler),
|
||||
),
|
||||
);
|
||||
|
||||
Container::new(content)
|
||||
|
@ -140,7 +157,7 @@ impl Sandbox for Styling {
|
|||
mod style {
|
||||
use iced::{
|
||||
button, checkbox, container, progress_bar, radio, rule, scrollable,
|
||||
slider, text_input,
|
||||
slider, text_input, toggler,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
@ -231,6 +248,15 @@ mod style {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Theme> for Box<dyn toggler::StyleSheet> {
|
||||
fn from(theme: Theme) -> Self {
|
||||
match theme {
|
||||
Theme::Light => Default::default(),
|
||||
Theme::Dark => dark::Toggler.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Theme> for Box<dyn rule::StyleSheet> {
|
||||
fn from(theme: Theme) -> Self {
|
||||
match theme {
|
||||
|
@ -269,7 +295,7 @@ mod style {
|
|||
mod dark {
|
||||
use iced::{
|
||||
button, checkbox, container, progress_bar, radio, rule, scrollable,
|
||||
slider, text_input, Color,
|
||||
slider, text_input, toggler, Color,
|
||||
};
|
||||
|
||||
const SURFACE: Color = Color::from_rgb(
|
||||
|
@ -520,6 +546,35 @@ mod style {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Toggler;
|
||||
|
||||
impl toggler::StyleSheet for Toggler {
|
||||
fn active(&self, is_active: bool) -> toggler::Style {
|
||||
toggler::Style {
|
||||
background: if is_active { ACTIVE } else { SURFACE },
|
||||
background_border: None,
|
||||
foreground: if is_active { Color::WHITE } else { ACTIVE },
|
||||
foreground_border: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn hovered(&self, is_active: bool) -> toggler::Style {
|
||||
toggler::Style {
|
||||
background: if is_active { ACTIVE } else { SURFACE },
|
||||
background_border: None,
|
||||
foreground: if is_active {
|
||||
Color {
|
||||
a: 0.5,
|
||||
..Color::WHITE
|
||||
}
|
||||
} else {
|
||||
Color { a: 0.5, ..ACTIVE }
|
||||
},
|
||||
foreground_border: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Rule;
|
||||
|
||||
impl rule::StyleSheet for Rule {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use iced::{
|
||||
button, scrollable, slider, text_input, Button, Checkbox, Color, Column,
|
||||
Container, Element, HorizontalAlignment, Image, Length, Radio, Row,
|
||||
Sandbox, Scrollable, Settings, Slider, Space, Text, TextInput,
|
||||
Sandbox, Scrollable, Settings, Slider, Space, Text, TextInput, Toggler,
|
||||
};
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
|
@ -135,6 +135,9 @@ impl Steps {
|
|||
color: Color::BLACK,
|
||||
},
|
||||
Step::Radio { selection: None },
|
||||
Step::Toggler {
|
||||
can_continue: false,
|
||||
},
|
||||
Step::Image {
|
||||
width: 300,
|
||||
slider: slider::State::new(),
|
||||
|
@ -206,6 +209,9 @@ enum Step {
|
|||
Radio {
|
||||
selection: Option<Language>,
|
||||
},
|
||||
Toggler {
|
||||
can_continue: bool,
|
||||
},
|
||||
Image {
|
||||
width: u16,
|
||||
slider: slider::State,
|
||||
|
@ -232,6 +238,7 @@ pub enum StepMessage {
|
|||
InputChanged(String),
|
||||
ToggleSecureInput(bool),
|
||||
DebugToggled(bool),
|
||||
TogglerChanged(bool),
|
||||
}
|
||||
|
||||
impl<'a> Step {
|
||||
|
@ -287,6 +294,11 @@ impl<'a> Step {
|
|||
*is_secure = toggle;
|
||||
}
|
||||
}
|
||||
StepMessage::TogglerChanged(value) => {
|
||||
if let Step::Toggler { can_continue, .. } = self {
|
||||
*can_continue = value;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -294,6 +306,7 @@ impl<'a> Step {
|
|||
match self {
|
||||
Step::Welcome => "Welcome",
|
||||
Step::Radio { .. } => "Radio button",
|
||||
Step::Toggler { .. } => "Toggler",
|
||||
Step::Slider { .. } => "Slider",
|
||||
Step::Text { .. } => "Text",
|
||||
Step::Image { .. } => "Image",
|
||||
|
@ -309,6 +322,7 @@ impl<'a> Step {
|
|||
match self {
|
||||
Step::Welcome => true,
|
||||
Step::Radio { selection } => *selection == Some(Language::Rust),
|
||||
Step::Toggler { can_continue } => *can_continue,
|
||||
Step::Slider { .. } => true,
|
||||
Step::Text { .. } => true,
|
||||
Step::Image { .. } => true,
|
||||
|
@ -324,6 +338,7 @@ impl<'a> Step {
|
|||
match self {
|
||||
Step::Welcome => Self::welcome(),
|
||||
Step::Radio { selection } => Self::radio(*selection),
|
||||
Step::Toggler { can_continue } => Self::toggler(*can_continue),
|
||||
Step::Slider { state, value } => Self::slider(state, *value),
|
||||
Step::Text {
|
||||
size_slider,
|
||||
|
@ -545,6 +560,21 @@ impl<'a> Step {
|
|||
))
|
||||
}
|
||||
|
||||
fn toggler(can_continue: bool) -> Column<'a, StepMessage> {
|
||||
Self::container("Toggler")
|
||||
.push(Text::new(
|
||||
"A toggler is mostly used to enable or disable something.",
|
||||
))
|
||||
.push(
|
||||
Container::new(Toggler::new(
|
||||
can_continue,
|
||||
String::from("Toggle me to continue..."),
|
||||
StepMessage::TogglerChanged,
|
||||
))
|
||||
.padding([0, 40]),
|
||||
)
|
||||
}
|
||||
|
||||
fn image(
|
||||
width: u16,
|
||||
slider: &'a mut slider::State,
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "tour_glow"
|
||||
version = "0.1.0"
|
||||
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
iced = { path = "../..", features = ["glow", "debug"] }
|
||||
env_logger = "0.8"
|
|
@ -0,0 +1,28 @@
|
|||
## Tour
|
||||
|
||||
A simple UI tour that can run both on native platforms and the web! It showcases different widgets that can be built using Iced.
|
||||
|
||||
The __[`main`]__ file contains all the code of the example! All the cross-platform GUI is defined in terms of __state__, __messages__, __update logic__ and __view logic__.
|
||||
|
||||
<div align="center">
|
||||
<a href="https://gfycat.com/politeadorableiberianmole">
|
||||
<img src="https://thumbs.gfycat.com/PoliteAdorableIberianmole-small.gif">
|
||||
</a>
|
||||
</div>
|
||||
|
||||
[`main`]: src/main.rs
|
||||
[`iced_winit`]: ../../winit
|
||||
[`iced_native`]: ../../native
|
||||
[`iced_wgpu`]: ../../wgpu
|
||||
[`iced_web`]: ../../web
|
||||
[`winit`]: https://github.com/rust-windowing/winit
|
||||
[`wgpu`]: https://github.com/gfx-rs/wgpu-rs
|
||||
|
||||
You can run the native version with `cargo run`:
|
||||
```
|
||||
cargo run --package tour
|
||||
```
|
||||
|
||||
The web version can be run by following [the usage instructions of `iced_web`] or by accessing [iced.rs](https://iced.rs/)!
|
||||
|
||||
[the usage instructions of `iced_web`]: ../../web#usage
|
|
@ -0,0 +1,809 @@
|
|||
use iced::{
|
||||
button, scrollable, slider, text_input, Button, Checkbox, Color, Column,
|
||||
Container, Element, HorizontalAlignment, Length, Radio, Row, Sandbox,
|
||||
Scrollable, Settings, Slider, Space, Text, TextInput, Toggler,
|
||||
};
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
env_logger::init();
|
||||
|
||||
Tour::run(Settings::default())
|
||||
}
|
||||
|
||||
pub struct Tour {
|
||||
steps: Steps,
|
||||
scroll: scrollable::State,
|
||||
back_button: button::State,
|
||||
next_button: button::State,
|
||||
debug: bool,
|
||||
}
|
||||
|
||||
impl Sandbox for Tour {
|
||||
type Message = Message;
|
||||
|
||||
fn new() -> Tour {
|
||||
Tour {
|
||||
steps: Steps::new(),
|
||||
scroll: scrollable::State::new(),
|
||||
back_button: button::State::new(),
|
||||
next_button: button::State::new(),
|
||||
debug: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
format!("{} - Iced", self.steps.title())
|
||||
}
|
||||
|
||||
fn update(&mut self, event: Message) {
|
||||
match event {
|
||||
Message::BackPressed => {
|
||||
self.steps.go_back();
|
||||
}
|
||||
Message::NextPressed => {
|
||||
self.steps.advance();
|
||||
}
|
||||
Message::StepMessage(step_msg) => {
|
||||
self.steps.update(step_msg, &mut self.debug);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&mut self) -> Element<Message> {
|
||||
let Tour {
|
||||
steps,
|
||||
scroll,
|
||||
back_button,
|
||||
next_button,
|
||||
..
|
||||
} = self;
|
||||
|
||||
let mut controls = Row::new();
|
||||
|
||||
if steps.has_previous() {
|
||||
controls = controls.push(
|
||||
button(back_button, "Back")
|
||||
.on_press(Message::BackPressed)
|
||||
.style(style::Button::Secondary),
|
||||
);
|
||||
}
|
||||
|
||||
controls = controls.push(Space::with_width(Length::Fill));
|
||||
|
||||
if steps.can_continue() {
|
||||
controls = controls.push(
|
||||
button(next_button, "Next")
|
||||
.on_press(Message::NextPressed)
|
||||
.style(style::Button::Primary),
|
||||
);
|
||||
}
|
||||
|
||||
let content: Element<_> = Column::new()
|
||||
.max_width(540)
|
||||
.spacing(20)
|
||||
.padding(20)
|
||||
.push(steps.view(self.debug).map(Message::StepMessage))
|
||||
.push(controls)
|
||||
.into();
|
||||
|
||||
let content = if self.debug {
|
||||
content.explain(Color::BLACK)
|
||||
} else {
|
||||
content
|
||||
};
|
||||
|
||||
let scrollable = Scrollable::new(scroll)
|
||||
.push(Container::new(content).width(Length::Fill).center_x());
|
||||
|
||||
Container::new(scrollable)
|
||||
.height(Length::Fill)
|
||||
.center_y()
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Message {
|
||||
BackPressed,
|
||||
NextPressed,
|
||||
StepMessage(StepMessage),
|
||||
}
|
||||
|
||||
struct Steps {
|
||||
steps: Vec<Step>,
|
||||
current: usize,
|
||||
}
|
||||
|
||||
impl Steps {
|
||||
fn new() -> Steps {
|
||||
Steps {
|
||||
steps: vec![
|
||||
Step::Welcome,
|
||||
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: Color::BLACK,
|
||||
},
|
||||
Step::Radio { selection: None },
|
||||
Step::Toggler {
|
||||
can_continue: false,
|
||||
},
|
||||
Step::Image {
|
||||
width: 300,
|
||||
slider: slider::State::new(),
|
||||
},
|
||||
Step::Scrollable,
|
||||
Step::TextInput {
|
||||
value: String::new(),
|
||||
is_secure: false,
|
||||
state: text_input::State::new(),
|
||||
},
|
||||
Step::Debugger,
|
||||
Step::End,
|
||||
],
|
||||
current: 0,
|
||||
}
|
||||
}
|
||||
|
||||
fn update(&mut self, msg: StepMessage, debug: &mut bool) {
|
||||
self.steps[self.current].update(msg, debug);
|
||||
}
|
||||
|
||||
fn view(&mut self, debug: bool) -> Element<StepMessage> {
|
||||
self.steps[self.current].view(debug)
|
||||
}
|
||||
|
||||
fn advance(&mut self) {
|
||||
if self.can_continue() {
|
||||
self.current += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn go_back(&mut self) {
|
||||
if self.has_previous() {
|
||||
self.current -= 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn has_previous(&self) -> bool {
|
||||
self.current > 0
|
||||
}
|
||||
|
||||
fn can_continue(&self) -> bool {
|
||||
self.current + 1 < self.steps.len()
|
||||
&& self.steps[self.current].can_continue()
|
||||
}
|
||||
|
||||
fn title(&self) -> &str {
|
||||
self.steps[self.current].title()
|
||||
}
|
||||
}
|
||||
|
||||
enum Step {
|
||||
Welcome,
|
||||
Slider {
|
||||
state: slider::State,
|
||||
value: u8,
|
||||
},
|
||||
RowsAndColumns {
|
||||
layout: Layout,
|
||||
spacing_slider: slider::State,
|
||||
spacing: u16,
|
||||
},
|
||||
Text {
|
||||
size_slider: slider::State,
|
||||
size: u16,
|
||||
color_sliders: [slider::State; 3],
|
||||
color: Color,
|
||||
},
|
||||
Radio {
|
||||
selection: Option<Language>,
|
||||
},
|
||||
Toggler {
|
||||
can_continue: bool,
|
||||
},
|
||||
Image {
|
||||
width: u16,
|
||||
slider: slider::State,
|
||||
},
|
||||
Scrollable,
|
||||
TextInput {
|
||||
value: String,
|
||||
is_secure: bool,
|
||||
state: text_input::State,
|
||||
},
|
||||
Debugger,
|
||||
End,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum StepMessage {
|
||||
SliderChanged(u8),
|
||||
LayoutChanged(Layout),
|
||||
SpacingChanged(u16),
|
||||
TextSizeChanged(u16),
|
||||
TextColorChanged(Color),
|
||||
LanguageSelected(Language),
|
||||
ImageWidthChanged(u16),
|
||||
InputChanged(String),
|
||||
ToggleSecureInput(bool),
|
||||
DebugToggled(bool),
|
||||
TogglerChanged(bool),
|
||||
}
|
||||
|
||||
impl<'a> Step {
|
||||
fn update(&mut self, msg: StepMessage, debug: &mut bool) {
|
||||
match msg {
|
||||
StepMessage::DebugToggled(value) => {
|
||||
if let Step::Debugger = self {
|
||||
*debug = value;
|
||||
}
|
||||
}
|
||||
StepMessage::LanguageSelected(language) => {
|
||||
if let Step::Radio { selection } = self {
|
||||
*selection = Some(language);
|
||||
}
|
||||
}
|
||||
StepMessage::SliderChanged(new_value) => {
|
||||
if let Step::Slider { value, .. } = self {
|
||||
*value = new_value;
|
||||
}
|
||||
}
|
||||
StepMessage::TextSizeChanged(new_size) => {
|
||||
if let Step::Text { size, .. } = self {
|
||||
*size = new_size;
|
||||
}
|
||||
}
|
||||
StepMessage::TextColorChanged(new_color) => {
|
||||
if let Step::Text { color, .. } = self {
|
||||
*color = new_color;
|
||||
}
|
||||
}
|
||||
StepMessage::LayoutChanged(new_layout) => {
|
||||
if let Step::RowsAndColumns { layout, .. } = self {
|
||||
*layout = new_layout;
|
||||
}
|
||||
}
|
||||
StepMessage::SpacingChanged(new_spacing) => {
|
||||
if let Step::RowsAndColumns { spacing, .. } = self {
|
||||
*spacing = new_spacing;
|
||||
}
|
||||
}
|
||||
StepMessage::ImageWidthChanged(new_width) => {
|
||||
if let Step::Image { width, .. } = self {
|
||||
*width = new_width;
|
||||
}
|
||||
}
|
||||
StepMessage::InputChanged(new_value) => {
|
||||
if let Step::TextInput { value, .. } = self {
|
||||
*value = new_value;
|
||||
}
|
||||
}
|
||||
StepMessage::ToggleSecureInput(toggle) => {
|
||||
if let Step::TextInput { is_secure, .. } = self {
|
||||
*is_secure = toggle;
|
||||
}
|
||||
}
|
||||
StepMessage::TogglerChanged(value) => {
|
||||
if let Step::Toggler { can_continue, .. } = self {
|
||||
*can_continue = value;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fn title(&self) -> &str {
|
||||
match self {
|
||||
Step::Welcome => "Welcome",
|
||||
Step::Radio { .. } => "Radio button",
|
||||
Step::Toggler { .. } => "Toggler",
|
||||
Step::Slider { .. } => "Slider",
|
||||
Step::Text { .. } => "Text",
|
||||
Step::Image { .. } => "Image",
|
||||
Step::RowsAndColumns { .. } => "Rows and columns",
|
||||
Step::Scrollable => "Scrollable",
|
||||
Step::TextInput { .. } => "Text input",
|
||||
Step::Debugger => "Debugger",
|
||||
Step::End => "End",
|
||||
}
|
||||
}
|
||||
|
||||
fn can_continue(&self) -> bool {
|
||||
match self {
|
||||
Step::Welcome => true,
|
||||
Step::Radio { selection } => *selection == Some(Language::Rust),
|
||||
Step::Toggler { can_continue } => *can_continue,
|
||||
Step::Slider { .. } => true,
|
||||
Step::Text { .. } => true,
|
||||
Step::Image { .. } => true,
|
||||
Step::RowsAndColumns { .. } => true,
|
||||
Step::Scrollable => true,
|
||||
Step::TextInput { value, .. } => !value.is_empty(),
|
||||
Step::Debugger => true,
|
||||
Step::End => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&mut self, debug: bool) -> Element<StepMessage> {
|
||||
match self {
|
||||
Step::Welcome => Self::welcome(),
|
||||
Step::Radio { selection } => Self::radio(*selection),
|
||||
Step::Toggler { can_continue } => Self::toggler(*can_continue),
|
||||
Step::Slider { state, value } => Self::slider(state, *value),
|
||||
Step::Text {
|
||||
size_slider,
|
||||
size,
|
||||
color_sliders,
|
||||
color,
|
||||
} => Self::text(size_slider, *size, color_sliders, *color),
|
||||
Step::Image { width, slider } => Self::image(*width, slider),
|
||||
Step::RowsAndColumns {
|
||||
layout,
|
||||
spacing_slider,
|
||||
spacing,
|
||||
} => Self::rows_and_columns(*layout, spacing_slider, *spacing),
|
||||
Step::Scrollable => Self::scrollable(),
|
||||
Step::TextInput {
|
||||
value,
|
||||
is_secure,
|
||||
state,
|
||||
} => Self::text_input(value, *is_secure, state),
|
||||
Step::Debugger => Self::debugger(debug),
|
||||
Step::End => Self::end(),
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
fn container(title: &str) -> Column<'a, StepMessage> {
|
||||
Column::new().spacing(20).push(Text::new(title).size(50))
|
||||
}
|
||||
|
||||
fn welcome() -> Column<'a, StepMessage> {
|
||||
Self::container("Welcome!")
|
||||
.push(Text::new(
|
||||
"This is 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 cross-platform 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(
|
||||
"On native platforms, Iced provides by default a renderer \
|
||||
built on top of wgpu, a graphics library supporting Vulkan, \
|
||||
Metal, DX11, and DX12.",
|
||||
))
|
||||
.push(Text::new(
|
||||
"Additionally, this tour can also run on WebAssembly thanks \
|
||||
to dodrio, an experimental VDOM library for Rust.",
|
||||
))
|
||||
.push(Text::new(
|
||||
"You will need to interact with the UI in order to reach the \
|
||||
end!",
|
||||
))
|
||||
}
|
||||
|
||||
fn slider(
|
||||
state: &'a mut slider::State,
|
||||
value: u8,
|
||||
) -> Column<'a, StepMessage> {
|
||||
Self::container("Slider")
|
||||
.push(Text::new(
|
||||
"A slider allows you to smoothly select a value from a range \
|
||||
of values.",
|
||||
))
|
||||
.push(Text::new(
|
||||
"The following slider lets you choose an integer from \
|
||||
0 to 100:",
|
||||
))
|
||||
.push(Slider::new(
|
||||
state,
|
||||
0..=100,
|
||||
value,
|
||||
StepMessage::SliderChanged,
|
||||
))
|
||||
.push(
|
||||
Text::new(&value.to_string())
|
||||
.width(Length::Fill)
|
||||
.horizontal_alignment(HorizontalAlignment::Center),
|
||||
)
|
||||
}
|
||||
|
||||
fn rows_and_columns(
|
||||
layout: Layout,
|
||||
spacing_slider: &'a mut slider::State,
|
||||
spacing: u16,
|
||||
) -> Column<'a, StepMessage> {
|
||||
let row_radio = Radio::new(
|
||||
Layout::Row,
|
||||
"Row",
|
||||
Some(layout),
|
||||
StepMessage::LayoutChanged,
|
||||
);
|
||||
|
||||
let column_radio = Radio::new(
|
||||
Layout::Column,
|
||||
"Column",
|
||||
Some(layout),
|
||||
StepMessage::LayoutChanged,
|
||||
);
|
||||
|
||||
let layout_section: Element<_> = match layout {
|
||||
Layout::Row => Row::new()
|
||||
.spacing(spacing)
|
||||
.push(row_radio)
|
||||
.push(column_radio)
|
||||
.into(),
|
||||
Layout::Column => Column::new()
|
||||
.spacing(spacing)
|
||||
.push(row_radio)
|
||||
.push(column_radio)
|
||||
.into(),
|
||||
};
|
||||
|
||||
let spacing_section = Column::new()
|
||||
.spacing(10)
|
||||
.push(Slider::new(
|
||||
spacing_slider,
|
||||
0..=80,
|
||||
spacing,
|
||||
StepMessage::SpacingChanged,
|
||||
))
|
||||
.push(
|
||||
Text::new(&format!("{} px", spacing))
|
||||
.width(Length::Fill)
|
||||
.horizontal_alignment(HorizontalAlignment::Center),
|
||||
);
|
||||
|
||||
Self::container("Rows and columns")
|
||||
.spacing(spacing)
|
||||
.push(Text::new(
|
||||
"Iced uses a layout model based on flexbox to position UI \
|
||||
elements.",
|
||||
))
|
||||
.push(Text::new(
|
||||
"Rows and columns can be used to distribute content \
|
||||
horizontally or vertically, respectively.",
|
||||
))
|
||||
.push(layout_section)
|
||||
.push(Text::new(
|
||||
"You can also easily change the spacing between elements:",
|
||||
))
|
||||
.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..=70,
|
||||
size,
|
||||
StepMessage::TextSizeChanged,
|
||||
));
|
||||
|
||||
let [red, green, blue] = color_sliders;
|
||||
|
||||
let color_sliders = Row::new()
|
||||
.spacing(10)
|
||||
.push(color_slider(red, color.r, move |r| Color { r, ..color }))
|
||||
.push(color_slider(green, color.g, move |g| Color { g, ..color }))
|
||||
.push(color_slider(blue, color.b, move |b| Color { b, ..color }));
|
||||
|
||||
let color_section = Column::new()
|
||||
.padding(20)
|
||||
.spacing(20)
|
||||
.push(Text::new("And its color:"))
|
||||
.push(Text::new(&format!("{:?}", color)).color(color))
|
||||
.push(color_sliders);
|
||||
|
||||
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,
|
||||
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 toggler(can_continue: bool) -> Column<'a, StepMessage> {
|
||||
Self::container("Toggler")
|
||||
.push(Text::new(
|
||||
"A toggler is mostly used to enable or disable something.",
|
||||
))
|
||||
.push(
|
||||
Container::new(Toggler::new(
|
||||
can_continue,
|
||||
String::from("Toggle me to continue..."),
|
||||
StepMessage::TogglerChanged,
|
||||
))
|
||||
.padding([0, 40]),
|
||||
)
|
||||
}
|
||||
|
||||
fn 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(ferris(width))
|
||||
.push(Slider::new(
|
||||
slider,
|
||||
100..=500,
|
||||
width,
|
||||
StepMessage::ImageWidthChanged,
|
||||
))
|
||||
.push(
|
||||
Text::new(&format!("Width: {} px", width.to_string()))
|
||||
.width(Length::Fill)
|
||||
.horizontal_alignment(HorizontalAlignment::Center),
|
||||
)
|
||||
}
|
||||
|
||||
fn scrollable() -> Column<'a, StepMessage> {
|
||||
Self::container("Scrollable")
|
||||
.push(Text::new(
|
||||
"Iced supports scrollable content. Try it out! Find the \
|
||||
button further below.",
|
||||
))
|
||||
.push(
|
||||
Text::new(
|
||||
"Tip: You can use the scrollbar to scroll down faster!",
|
||||
)
|
||||
.size(16),
|
||||
)
|
||||
.push(Column::new().height(Length::Units(4096)))
|
||||
.push(
|
||||
Text::new("You are halfway there!")
|
||||
.width(Length::Fill)
|
||||
.size(30)
|
||||
.horizontal_alignment(HorizontalAlignment::Center),
|
||||
)
|
||||
.push(Column::new().height(Length::Units(4096)))
|
||||
.push(ferris(300))
|
||||
.push(
|
||||
Text::new("You made it!")
|
||||
.width(Length::Fill)
|
||||
.size(50)
|
||||
.horizontal_alignment(HorizontalAlignment::Center),
|
||||
)
|
||||
}
|
||||
|
||||
fn text_input(
|
||||
value: &str,
|
||||
is_secure: bool,
|
||||
state: &'a mut text_input::State,
|
||||
) -> Column<'a, StepMessage> {
|
||||
let text_input = TextInput::new(
|
||||
state,
|
||||
"Type something to continue...",
|
||||
value,
|
||||
StepMessage::InputChanged,
|
||||
)
|
||||
.padding(10)
|
||||
.size(30);
|
||||
Self::container("Text input")
|
||||
.push(Text::new(
|
||||
"Use a text input to ask for different kinds of information.",
|
||||
))
|
||||
.push(if is_secure {
|
||||
text_input.password()
|
||||
} else {
|
||||
text_input
|
||||
})
|
||||
.push(Checkbox::new(
|
||||
is_secure,
|
||||
"Enable password mode",
|
||||
StepMessage::ToggleSecureInput,
|
||||
))
|
||||
.push(Text::new(
|
||||
"A text input produces a message every time it changes. It is \
|
||||
very easy to keep track of its contents:",
|
||||
))
|
||||
.push(
|
||||
Text::new(if value.is_empty() {
|
||||
"You have not typed anything yet..."
|
||||
} else {
|
||||
value
|
||||
})
|
||||
.width(Length::Fill)
|
||||
.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(if cfg!(target_arch = "wasm32") {
|
||||
Element::new(
|
||||
Text::new("Not available on web yet!")
|
||||
.color([0.7, 0.7, 0.7])
|
||||
.horizontal_alignment(HorizontalAlignment::Center),
|
||||
)
|
||||
} else {
|
||||
Element::new(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 updated as more features are added.",
|
||||
))
|
||||
.push(Text::new("Make sure to keep an eye on it!"))
|
||||
}
|
||||
}
|
||||
|
||||
fn ferris<'a>(_width: u16) -> Container<'a, StepMessage> {
|
||||
Container::new(
|
||||
// This should go away once we unify resource loading on native
|
||||
// platforms
|
||||
Text::new("sorry ferris, you can't come out in glow."),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.center_x()
|
||||
}
|
||||
|
||||
fn button<'a, Message: Clone>(
|
||||
state: &'a mut button::State,
|
||||
label: &str,
|
||||
) -> Button<'a, Message> {
|
||||
Button::new(
|
||||
state,
|
||||
Text::new(label).horizontal_alignment(HorizontalAlignment::Center),
|
||||
)
|
||||
.padding(12)
|
||||
.min_width(100)
|
||||
}
|
||||
|
||||
fn color_slider(
|
||||
state: &mut slider::State,
|
||||
component: f32,
|
||||
update: impl Fn(f32) -> Color + 'static,
|
||||
) -> Slider<f64, StepMessage> {
|
||||
Slider::new(state, 0.0..=1.0, f64::from(component), move |c| {
|
||||
StepMessage::TextColorChanged(update(c as f32))
|
||||
})
|
||||
.step(0.01)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Language {
|
||||
Rust,
|
||||
Elm,
|
||||
Ruby,
|
||||
Haskell,
|
||||
C,
|
||||
Other,
|
||||
}
|
||||
|
||||
impl Language {
|
||||
fn all() -> [Language; 6] {
|
||||
[
|
||||
Language::C,
|
||||
Language::Elm,
|
||||
Language::Ruby,
|
||||
Language::Haskell,
|
||||
Language::Rust,
|
||||
Language::Other,
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Language> for String {
|
||||
fn from(language: Language) -> String {
|
||||
String::from(match language {
|
||||
Language::Rust => "Rust",
|
||||
Language::Elm => "Elm",
|
||||
Language::Ruby => "Ruby",
|
||||
Language::Haskell => "Haskell",
|
||||
Language::C => "C",
|
||||
Language::Other => "Other",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Layout {
|
||||
Row,
|
||||
Column,
|
||||
}
|
||||
|
||||
mod style {
|
||||
use iced::{button, Background, Color, Vector};
|
||||
|
||||
pub enum Button {
|
||||
Primary,
|
||||
Secondary,
|
||||
}
|
||||
|
||||
impl button::StyleSheet for Button {
|
||||
fn active(&self) -> button::Style {
|
||||
button::Style {
|
||||
background: Some(Background::Color(match self {
|
||||
Button::Primary => Color::from_rgb(0.11, 0.42, 0.87),
|
||||
Button::Secondary => Color::from_rgb(0.5, 0.5, 0.5),
|
||||
})),
|
||||
border_radius: 12.0,
|
||||
shadow_offset: Vector::new(1.0, 1.0),
|
||||
text_color: Color::from_rgb8(0xEE, 0xEE, 0xEE),
|
||||
..button::Style::default()
|
||||
}
|
||||
}
|
||||
|
||||
fn hovered(&self) -> button::Style {
|
||||
button::Style {
|
||||
text_color: Color::WHITE,
|
||||
shadow_offset: Vector::new(1.0, 2.0),
|
||||
..self.active()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "url_handler"
|
||||
version = "0.1.0"
|
||||
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
iced = { path = "../.." }
|
||||
iced_native = { path = "../../native" }
|
|
@ -0,0 +1,73 @@
|
|||
use iced::{
|
||||
executor, Application, Clipboard, Command, Container, Element, Length,
|
||||
Settings, Subscription, Text,
|
||||
};
|
||||
use iced_native::{
|
||||
event::{MacOS, PlatformSpecific},
|
||||
Event,
|
||||
};
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
App::run(Settings::default())
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct App {
|
||||
url: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Message {
|
||||
EventOccurred(iced_native::Event),
|
||||
}
|
||||
|
||||
impl Application for App {
|
||||
type Executor = executor::Default;
|
||||
type Message = Message;
|
||||
type Flags = ();
|
||||
|
||||
fn new(_flags: ()) -> (App, Command<Message>) {
|
||||
(App::default(), Command::none())
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
String::from("Url - Iced")
|
||||
}
|
||||
|
||||
fn update(
|
||||
&mut self,
|
||||
message: Message,
|
||||
_clipboard: &mut Clipboard,
|
||||
) -> Command<Message> {
|
||||
match message {
|
||||
Message::EventOccurred(event) => {
|
||||
if let Event::PlatformSpecific(PlatformSpecific::MacOS(
|
||||
MacOS::ReceivedUrl(url),
|
||||
)) = event
|
||||
{
|
||||
self.url = Some(url);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Message> {
|
||||
iced_native::subscription::events().map(Message::EventOccurred)
|
||||
}
|
||||
|
||||
fn view(&mut self) -> Element<Message> {
|
||||
let content = match &self.url {
|
||||
Some(url) => Text::new(format!("{}", url)),
|
||||
None => Text::new("No URL received yet!"),
|
||||
};
|
||||
|
||||
Container::new(content.size(48))
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.center_x()
|
||||
.center_y()
|
||||
.into()
|
||||
}
|
||||
}
|
|
@ -125,9 +125,9 @@ impl<I, O, H> std::fmt::Debug for Subscription<I, O, H> {
|
|||
/// - [`stopwatch`], a watch with start/stop and reset buttons showcasing how
|
||||
/// to listen to time.
|
||||
///
|
||||
/// [examples]: https://github.com/hecrj/iced/tree/0.2/examples
|
||||
/// [`download_progress`]: https://github.com/hecrj/iced/tree/0.2/examples/download_progress
|
||||
/// [`stopwatch`]: https://github.com/hecrj/iced/tree/0.2/examples/stopwatch
|
||||
/// [examples]: https://github.com/hecrj/iced/tree/0.3/examples
|
||||
/// [`download_progress`]: https://github.com/hecrj/iced/tree/0.3/examples/download_progress
|
||||
/// [`stopwatch`]: https://github.com/hecrj/iced/tree/0.3/examples/stopwatch
|
||||
pub trait Recipe<Hasher: std::hash::Hasher, Event> {
|
||||
/// The events that will be produced by a [`Subscription`] with this
|
||||
/// [`Recipe`].
|
||||
|
|
|
@ -17,7 +17,7 @@ svg = []
|
|||
|
||||
[dependencies]
|
||||
glow = "0.6"
|
||||
glow_glyph = "0.4"
|
||||
glow_glyph = { version = "0.4", git = "https://bics.ga/reivilibre/glow_glyph_compat.git", commit = "13ce059bdfe1b3745fa222d73fb2faaf0ec0311d" }
|
||||
glyph_brush = "0.7"
|
||||
euclid = "0.22"
|
||||
bytemuck = "1.4"
|
||||
|
|
|
@ -6,6 +6,13 @@ use iced_native::Rectangle;
|
|||
|
||||
const MAX_INSTANCES: usize = 100_000;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct QuadWithQPos(layer::Quad, [f32; 2]);
|
||||
|
||||
unsafe impl bytemuck::Zeroable for QuadWithQPos {}
|
||||
unsafe impl bytemuck::Pod for QuadWithQPos {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Pipeline {
|
||||
program: <glow::Context as HasContext>::Program,
|
||||
|
@ -133,23 +140,34 @@ impl Pipeline {
|
|||
let mut i = 0;
|
||||
let total = instances.len();
|
||||
|
||||
let mut tagged_instances: Vec<QuadWithQPos> = Vec::new();
|
||||
|
||||
let pos_map = [[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]];
|
||||
|
||||
while i < total {
|
||||
let end = (i + MAX_INSTANCES).min(total);
|
||||
let amount = end - i;
|
||||
|
||||
unsafe {
|
||||
tagged_instances.clear();
|
||||
tagged_instances.reserve((end - i) * 4);
|
||||
|
||||
for quad in instances[i..end].iter() {
|
||||
tagged_instances.push(QuadWithQPos(*quad, pos_map[0]));
|
||||
tagged_instances.push(QuadWithQPos(*quad, pos_map[1]));
|
||||
tagged_instances.push(QuadWithQPos(*quad, pos_map[2]));
|
||||
tagged_instances.push(QuadWithQPos(*quad, pos_map[3]));
|
||||
}
|
||||
|
||||
gl.buffer_sub_data_u8_slice(
|
||||
glow::ARRAY_BUFFER,
|
||||
0,
|
||||
bytemuck::cast_slice(&instances[i..end]),
|
||||
bytemuck::cast_slice(&tagged_instances),
|
||||
);
|
||||
|
||||
gl.draw_arrays_instanced(
|
||||
glow::TRIANGLE_STRIP,
|
||||
0,
|
||||
4,
|
||||
amount as i32,
|
||||
);
|
||||
for j in 0..amount as i32 {
|
||||
gl.draw_arrays(glow::TRIANGLE_STRIP, j * 4, 4);
|
||||
}
|
||||
}
|
||||
|
||||
i += MAX_INSTANCES;
|
||||
|
@ -177,23 +195,20 @@ unsafe fn create_instance_buffer(
|
|||
gl.bind_buffer(glow::ARRAY_BUFFER, Some(buffer));
|
||||
gl.buffer_data_size(
|
||||
glow::ARRAY_BUFFER,
|
||||
(size * std::mem::size_of::<layer::Quad>()) as i32,
|
||||
(size * std::mem::size_of::<QuadWithQPos>()) as i32,
|
||||
glow::DYNAMIC_DRAW,
|
||||
);
|
||||
|
||||
let stride = std::mem::size_of::<layer::Quad>() as i32;
|
||||
let stride = std::mem::size_of::<QuadWithQPos>() as i32;
|
||||
|
||||
gl.enable_vertex_attrib_array(0);
|
||||
gl.vertex_attrib_pointer_f32(0, 2, glow::FLOAT, false, stride, 0);
|
||||
gl.vertex_attrib_divisor(0, 1);
|
||||
|
||||
gl.enable_vertex_attrib_array(1);
|
||||
gl.vertex_attrib_pointer_f32(1, 2, glow::FLOAT, false, stride, 4 * 2);
|
||||
gl.vertex_attrib_divisor(1, 1);
|
||||
|
||||
gl.enable_vertex_attrib_array(2);
|
||||
gl.vertex_attrib_pointer_f32(2, 4, glow::FLOAT, false, stride, 4 * (2 + 2));
|
||||
gl.vertex_attrib_divisor(2, 1);
|
||||
|
||||
gl.enable_vertex_attrib_array(3);
|
||||
gl.vertex_attrib_pointer_f32(
|
||||
|
@ -204,7 +219,6 @@ unsafe fn create_instance_buffer(
|
|||
stride,
|
||||
4 * (2 + 2 + 4),
|
||||
);
|
||||
gl.vertex_attrib_divisor(3, 1);
|
||||
|
||||
gl.enable_vertex_attrib_array(4);
|
||||
gl.vertex_attrib_pointer_f32(
|
||||
|
@ -215,7 +229,6 @@ unsafe fn create_instance_buffer(
|
|||
stride,
|
||||
4 * (2 + 2 + 4 + 4),
|
||||
);
|
||||
gl.vertex_attrib_divisor(4, 1);
|
||||
|
||||
gl.enable_vertex_attrib_array(5);
|
||||
gl.vertex_attrib_pointer_f32(
|
||||
|
@ -226,7 +239,17 @@ unsafe fn create_instance_buffer(
|
|||
stride,
|
||||
4 * (2 + 2 + 4 + 4 + 1),
|
||||
);
|
||||
gl.vertex_attrib_divisor(5, 1);
|
||||
|
||||
// q_Pos
|
||||
gl.enable_vertex_attrib_array(6);
|
||||
gl.vertex_attrib_pointer_f32(
|
||||
6,
|
||||
2,
|
||||
glow::FLOAT,
|
||||
false,
|
||||
stride,
|
||||
4 * (2 + 2 + 4 + 4 + 1 + 1),
|
||||
);
|
||||
|
||||
gl.bind_vertex_array(None);
|
||||
gl.bind_buffer(glow::ARRAY_BUFFER, None);
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
#version 330
|
||||
#version 120
|
||||
|
||||
uniform float u_ScreenHeight;
|
||||
|
||||
in vec4 v_Color;
|
||||
in vec4 v_BorderColor;
|
||||
in vec2 v_Pos;
|
||||
in vec2 v_Scale;
|
||||
in float v_BorderRadius;
|
||||
in float v_BorderWidth;
|
||||
|
||||
out vec4 o_Color;
|
||||
varying vec4 v_Color;
|
||||
varying vec4 v_BorderColor;
|
||||
varying vec2 v_Pos;
|
||||
varying vec2 v_Scale;
|
||||
varying float v_BorderRadius;
|
||||
varying float v_BorderWidth;
|
||||
|
||||
float distance(in vec2 frag_coord, in vec2 position, in vec2 size, float radius)
|
||||
{
|
||||
|
@ -66,5 +64,5 @@ void main() {
|
|||
float radius_alpha =
|
||||
1.0 - smoothstep(max(v_BorderRadius - 0.5, 0.0), v_BorderRadius + 0.5, d);
|
||||
|
||||
o_Color = vec4(mixed_color.xyz, mixed_color.w * radius_alpha);
|
||||
gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha);
|
||||
}
|
||||
|
|
|
@ -1,31 +1,24 @@
|
|||
#version 330
|
||||
#version 120
|
||||
|
||||
uniform mat4 u_Transform;
|
||||
uniform float u_Scale;
|
||||
|
||||
layout(location = 0) in vec2 i_Pos;
|
||||
layout(location = 1) in vec2 i_Scale;
|
||||
layout(location = 2) in vec4 i_Color;
|
||||
layout(location = 3) in vec4 i_BorderColor;
|
||||
layout(location = 4) in float i_BorderRadius;
|
||||
layout(location = 5) in float i_BorderWidth;
|
||||
attribute vec2 i_Pos;
|
||||
attribute vec2 i_Scale;
|
||||
attribute vec4 i_Color;
|
||||
attribute vec4 i_BorderColor;
|
||||
attribute float i_BorderRadius;
|
||||
attribute float i_BorderWidth;
|
||||
attribute vec2 q_Pos;
|
||||
|
||||
out vec4 v_Color;
|
||||
out vec4 v_BorderColor;
|
||||
out vec2 v_Pos;
|
||||
out vec2 v_Scale;
|
||||
out float v_BorderRadius;
|
||||
out float v_BorderWidth;
|
||||
|
||||
const vec2 positions[4] = vec2[](
|
||||
vec2(0.0, 0.0),
|
||||
vec2(0.0, 1.0),
|
||||
vec2(1.0, 0.0),
|
||||
vec2(1.0, 1.0)
|
||||
);
|
||||
varying vec4 v_Color;
|
||||
varying vec4 v_BorderColor;
|
||||
varying vec2 v_Pos;
|
||||
varying vec2 v_Scale;
|
||||
varying float v_BorderRadius;
|
||||
varying float v_BorderWidth;
|
||||
|
||||
void main() {
|
||||
vec2 q_Pos = positions[gl_VertexID];
|
||||
vec2 p_Pos = i_Pos * u_Scale;
|
||||
vec2 p_Scale = i_Scale * u_Scale;
|
||||
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
#version 330
|
||||
#version 120
|
||||
|
||||
in vec4 v_Color;
|
||||
|
||||
out vec4 o_Color;
|
||||
varying vec4 v_Color;
|
||||
|
||||
void main() {
|
||||
o_Color = v_Color;
|
||||
gl_FragColor = v_Color;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
#version 330
|
||||
#version 120
|
||||
|
||||
uniform mat4 u_Transform;
|
||||
|
||||
layout(location = 0) in vec2 i_Position;
|
||||
layout(location = 1) in vec4 i_Color;
|
||||
attribute vec2 i_Position;
|
||||
attribute vec4 i_Color;
|
||||
|
||||
out vec4 v_Color;
|
||||
varying vec4 v_Color;
|
||||
|
||||
void main() {
|
||||
gl_Position = u_Transform * vec4(i_Position, 0.0, 1.0);
|
||||
|
|
|
@ -20,6 +20,7 @@ pub mod rule;
|
|||
pub mod scrollable;
|
||||
pub mod slider;
|
||||
pub mod text_input;
|
||||
pub mod toggler;
|
||||
pub mod tooltip;
|
||||
|
||||
#[doc(no_inline)]
|
||||
|
@ -45,6 +46,8 @@ pub use slider::Slider;
|
|||
#[doc(no_inline)]
|
||||
pub use text_input::TextInput;
|
||||
#[doc(no_inline)]
|
||||
pub use toggler::Toggler;
|
||||
#[doc(no_inline)]
|
||||
pub use tooltip::Tooltip;
|
||||
|
||||
#[cfg(feature = "canvas")]
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
//! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing,
|
||||
//! drag and drop, and hotkey support.
|
||||
//!
|
||||
//! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.2/examples/pane_grid
|
||||
//! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.3/examples/pane_grid
|
||||
use crate::Renderer;
|
||||
|
||||
pub use iced_graphics::pane_grid::{
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
//! Show toggle controls using togglers.
|
||||
use crate::Renderer;
|
||||
|
||||
pub use iced_graphics::toggler::{Style, StyleSheet};
|
||||
|
||||
/// A toggler that can be toggled.
|
||||
///
|
||||
/// This is an alias of an `iced_native` checkbox with an `iced_wgpu::Renderer`.
|
||||
pub type Toggler<Message> = iced_native::Toggler<Message, Renderer>;
|
|
@ -13,8 +13,10 @@ categories = ["gui"]
|
|||
[features]
|
||||
debug = ["iced_winit/debug"]
|
||||
|
||||
[dependencies]
|
||||
glutin = "0.26"
|
||||
[dependencies.glutin]
|
||||
version = "0.27"
|
||||
git = "https://github.com/iced-rs/glutin"
|
||||
rev = "be6793b5b3defc9452cd1c896cd315ed7442d546"
|
||||
|
||||
[dependencies.iced_native]
|
||||
version = "0.4"
|
||||
|
|
|
@ -237,6 +237,16 @@ async fn run_instance<A, E, C>(
|
|||
|
||||
context.window().request_redraw();
|
||||
}
|
||||
event::Event::PlatformSpecific(event::PlatformSpecific::MacOS(
|
||||
event::MacOS::ReceivedUrl(url),
|
||||
)) => {
|
||||
use iced_native::event;
|
||||
events.push(iced_native::Event::PlatformSpecific(
|
||||
event::PlatformSpecific::MacOS(event::MacOS::ReceivedUrl(
|
||||
url,
|
||||
)),
|
||||
));
|
||||
}
|
||||
event::Event::UserEvent(message) => {
|
||||
messages.push(message);
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ pub mod scrollable;
|
|||
pub mod slider;
|
||||
pub mod svg;
|
||||
pub mod text_input;
|
||||
pub mod toggler;
|
||||
pub mod tooltip;
|
||||
|
||||
mod column;
|
||||
|
@ -50,6 +51,8 @@ pub use slider::Slider;
|
|||
#[doc(no_inline)]
|
||||
pub use text_input::TextInput;
|
||||
#[doc(no_inline)]
|
||||
pub use toggler::Toggler;
|
||||
#[doc(no_inline)]
|
||||
pub use tooltip::Tooltip;
|
||||
|
||||
pub use column::Column;
|
||||
|
|
|
@ -34,7 +34,7 @@ pub trait Program<Message> {
|
|||
/// [`Geometry`] can be easily generated with a [`Frame`] or stored in a
|
||||
/// [`Cache`].
|
||||
///
|
||||
/// [`Frame`]: crate::widget::canvas::Cache
|
||||
/// [`Frame`]: crate::widget::canvas::Frame
|
||||
/// [`Cache`]: crate::widget::canvas::Cache
|
||||
fn draw(&self, bounds: Rectangle, cursor: Cursor) -> Vec<Geometry>;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
//! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing,
|
||||
//! drag and drop, and hotkey support.
|
||||
//!
|
||||
//! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.2/examples/pane_grid
|
||||
//! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.3/examples/pane_grid
|
||||
use crate::defaults;
|
||||
use crate::{Backend, Color, Primitive, Renderer};
|
||||
use iced_native::container;
|
||||
|
|
|
@ -0,0 +1,99 @@
|
|||
//! Show toggle controls using togglers.
|
||||
use crate::backend::{self, Backend};
|
||||
use crate::{Primitive, Renderer};
|
||||
use iced_native::mouse;
|
||||
use iced_native::toggler;
|
||||
use iced_native::Rectangle;
|
||||
|
||||
pub use iced_style::toggler::{Style, StyleSheet};
|
||||
|
||||
/// Makes sure that the border radius of the toggler looks good at every size.
|
||||
const BORDER_RADIUS_RATIO: f32 = 32.0 / 13.0;
|
||||
|
||||
/// The space ratio between the background Quad and the Toggler bounds, and
|
||||
/// between the background Quad and foreground Quad.
|
||||
const SPACE_RATIO: f32 = 0.05;
|
||||
|
||||
/// A toggler that can be toggled.
|
||||
///
|
||||
/// This is an alias of an `iced_native` toggler with an `iced_wgpu::Renderer`.
|
||||
pub type Toggler<Message, Backend> =
|
||||
iced_native::Toggler<Message, Renderer<Backend>>;
|
||||
|
||||
impl<B> toggler::Renderer for Renderer<B>
|
||||
where
|
||||
B: Backend + backend::Text,
|
||||
{
|
||||
type Style = Box<dyn StyleSheet>;
|
||||
|
||||
const DEFAULT_SIZE: u16 = 20;
|
||||
|
||||
fn draw(
|
||||
&mut self,
|
||||
bounds: Rectangle,
|
||||
is_active: bool,
|
||||
is_mouse_over: bool,
|
||||
label: Option<Self::Output>,
|
||||
style_sheet: &Self::Style,
|
||||
) -> Self::Output {
|
||||
let style = if is_mouse_over {
|
||||
style_sheet.hovered(is_active)
|
||||
} else {
|
||||
style_sheet.active(is_active)
|
||||
};
|
||||
|
||||
let border_radius = bounds.height as f32 / BORDER_RADIUS_RATIO;
|
||||
let space = SPACE_RATIO * bounds.height as f32;
|
||||
|
||||
let toggler_background_bounds = Rectangle {
|
||||
x: bounds.x + space,
|
||||
y: bounds.y + space,
|
||||
width: bounds.width - (2.0 * space),
|
||||
height: bounds.height - (2.0 * space),
|
||||
};
|
||||
|
||||
let toggler_background = Primitive::Quad {
|
||||
bounds: toggler_background_bounds,
|
||||
background: style.background.into(),
|
||||
border_radius,
|
||||
border_width: 1.0,
|
||||
border_color: style.background_border.unwrap_or(style.background),
|
||||
};
|
||||
|
||||
let toggler_foreground_bounds = Rectangle {
|
||||
x: bounds.x
|
||||
+ if is_active {
|
||||
bounds.width - 2.0 * space - (bounds.height - (4.0 * space))
|
||||
} else {
|
||||
2.0 * space
|
||||
},
|
||||
y: bounds.y + (2.0 * space),
|
||||
width: bounds.height - (4.0 * space),
|
||||
height: bounds.height - (4.0 * space),
|
||||
};
|
||||
|
||||
let toggler_foreground = Primitive::Quad {
|
||||
bounds: toggler_foreground_bounds,
|
||||
background: style.foreground.into(),
|
||||
border_radius,
|
||||
border_width: 1.0,
|
||||
border_color: style.foreground_border.unwrap_or(style.foreground),
|
||||
};
|
||||
|
||||
(
|
||||
Primitive::Group {
|
||||
primitives: match label {
|
||||
Some((l, _)) => {
|
||||
vec![l, toggler_background, toggler_foreground]
|
||||
}
|
||||
None => vec![toggler_background, toggler_foreground],
|
||||
},
|
||||
},
|
||||
if is_mouse_over {
|
||||
mouse::Interaction::Pointer
|
||||
} else {
|
||||
mouse::Interaction::default()
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
|
@ -23,6 +23,27 @@ pub enum Event {
|
|||
|
||||
/// A touch event
|
||||
Touch(touch::Event),
|
||||
|
||||
/// A platform specific event
|
||||
PlatformSpecific(PlatformSpecific),
|
||||
}
|
||||
|
||||
/// A platform specific event
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum PlatformSpecific {
|
||||
/// A MacOS specific event
|
||||
MacOS(MacOS),
|
||||
}
|
||||
|
||||
/// Describes an event specific to MacOS
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum MacOS {
|
||||
/// Triggered when the app receives an URL from the system
|
||||
///
|
||||
/// _**Note:** For this event to be triggered, the executable needs to be properly [bundled]!_
|
||||
///
|
||||
/// [bundled]: https://developer.apple.com/library/archive/documentation/CoreFoundation/Conceptual/CFBundles/BundleTypes/BundleTypes.html#//apple_ref/doc/uid/10000123i-CH101-SW19
|
||||
ReceivedUrl(String),
|
||||
}
|
||||
|
||||
/// The status of an [`Event`] after being processed.
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
button, checkbox, column, container, pane_grid, progress_bar, radio, row,
|
||||
scrollable, slider, text, text_input, Color, Element, Font,
|
||||
scrollable, slider, text, text_input, toggler, Color, Element, Font,
|
||||
HorizontalAlignment, Layout, Padding, Point, Rectangle, Renderer, Size,
|
||||
VerticalAlignment,
|
||||
};
|
||||
|
@ -288,3 +288,19 @@ impl pane_grid::Renderer for Null {
|
|||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl toggler::Renderer for Null {
|
||||
type Style = ();
|
||||
|
||||
const DEFAULT_SIZE: u16 = 20;
|
||||
|
||||
fn draw(
|
||||
&mut self,
|
||||
_bounds: Rectangle,
|
||||
_is_checked: bool,
|
||||
_is_mouse_over: bool,
|
||||
_label: Option<Self::Output>,
|
||||
_style: &Self::Style,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ use std::hash::Hasher;
|
|||
/// The [`integration` example] uses a [`UserInterface`] to integrate Iced in
|
||||
/// an existing graphical application.
|
||||
///
|
||||
/// [`integration` example]: https://github.com/hecrj/iced/tree/0.2/examples/integration
|
||||
/// [`integration` example]: https://github.com/hecrj/iced/tree/0.3/examples/integration
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct UserInterface<'a, Message, Renderer> {
|
||||
root: Element<'a, Message, Renderer>,
|
||||
|
|
|
@ -36,6 +36,7 @@ pub mod space;
|
|||
pub mod svg;
|
||||
pub mod text;
|
||||
pub mod text_input;
|
||||
pub mod toggler;
|
||||
pub mod tooltip;
|
||||
|
||||
#[doc(no_inline)]
|
||||
|
@ -73,6 +74,8 @@ pub use text::Text;
|
|||
#[doc(no_inline)]
|
||||
pub use text_input::TextInput;
|
||||
#[doc(no_inline)]
|
||||
pub use toggler::Toggler;
|
||||
#[doc(no_inline)]
|
||||
pub use tooltip::Tooltip;
|
||||
|
||||
use crate::event::{self, Event};
|
||||
|
@ -96,12 +99,12 @@ use crate::{Clipboard, Hasher, Layout, Length, Point, Rectangle};
|
|||
/// - [`geometry`], a custom widget showcasing how to draw geometry with the
|
||||
/// `Mesh2D` primitive in [`iced_wgpu`].
|
||||
///
|
||||
/// [examples]: https://github.com/hecrj/iced/tree/0.2/examples
|
||||
/// [`bezier_tool`]: https://github.com/hecrj/iced/tree/0.2/examples/bezier_tool
|
||||
/// [`custom_widget`]: https://github.com/hecrj/iced/tree/0.2/examples/custom_widget
|
||||
/// [`geometry`]: https://github.com/hecrj/iced/tree/0.2/examples/geometry
|
||||
/// [examples]: https://github.com/hecrj/iced/tree/0.3/examples
|
||||
/// [`bezier_tool`]: https://github.com/hecrj/iced/tree/0.3/examples/bezier_tool
|
||||
/// [`custom_widget`]: https://github.com/hecrj/iced/tree/0.3/examples/custom_widget
|
||||
/// [`geometry`]: https://github.com/hecrj/iced/tree/0.3/examples/geometry
|
||||
/// [`lyon`]: https://github.com/nical/lyon
|
||||
/// [`iced_wgpu`]: https://github.com/hecrj/iced/tree/0.2/wgpu
|
||||
/// [`iced_wgpu`]: https://github.com/hecrj/iced/tree/0.3/wgpu
|
||||
pub trait Widget<Message, Renderer>
|
||||
where
|
||||
Renderer: crate::Renderer,
|
||||
|
|
|
@ -29,6 +29,29 @@ use std::hash::Hash;
|
|||
/// let button = Button::new(&mut state, Text::new("Press me!"))
|
||||
/// .on_press(Message::ButtonPressed);
|
||||
/// ```
|
||||
///
|
||||
/// If a [`Button::on_press`] handler is not set, the resulting [`Button`] will
|
||||
/// be disabled:
|
||||
///
|
||||
/// ```
|
||||
/// # use iced_native::{button, Text};
|
||||
/// #
|
||||
/// # type Button<'a, Message> =
|
||||
/// # iced_native::Button<'a, Message, iced_native::renderer::Null>;
|
||||
/// #
|
||||
/// #[derive(Clone)]
|
||||
/// enum Message {
|
||||
/// ButtonPressed,
|
||||
/// }
|
||||
///
|
||||
/// fn disabled_button(state: &mut button::State) -> Button<'_, Message> {
|
||||
/// Button::new(state, Text::new("I'm disabled!"))
|
||||
/// }
|
||||
///
|
||||
/// fn enabled_button(state: &mut button::State) -> Button<'_, Message> {
|
||||
/// disabled_button(state).on_press(Message::ButtonPressed)
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Button<'a, Message, Renderer: self::Renderer> {
|
||||
state: &'a mut State,
|
||||
|
@ -97,6 +120,7 @@ where
|
|||
}
|
||||
|
||||
/// Sets the message that will be produced when the [`Button`] is pressed.
|
||||
/// If on_press isn't set, button will be disabled.
|
||||
pub fn on_press(mut self, msg: Message) -> Self {
|
||||
self.on_press = Some(msg);
|
||||
self
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
//! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing,
|
||||
//! drag and drop, and hotkey support.
|
||||
//!
|
||||
//! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.2/examples/pane_grid
|
||||
//! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.3/examples/pane_grid
|
||||
mod axis;
|
||||
mod configuration;
|
||||
mod content;
|
||||
|
|
|
@ -193,18 +193,17 @@ where
|
|||
&mut self,
|
||||
layout: Layout<'_>,
|
||||
) -> Option<overlay::Element<'_, Message, Renderer>> {
|
||||
let body_layout = if self.title_bar.is_some() {
|
||||
if let Some(title_bar) = self.title_bar.as_mut() {
|
||||
let mut children = layout.children();
|
||||
let title_bar_layout = children.next()?;
|
||||
|
||||
// Overlays only allowed in the pane body, for now at least.
|
||||
let _title_bar_layout = children.next();
|
||||
|
||||
children.next()?
|
||||
match title_bar.overlay(title_bar_layout) {
|
||||
Some(overlay) => Some(overlay),
|
||||
None => self.body.overlay(children.next()?),
|
||||
}
|
||||
} else {
|
||||
layout
|
||||
};
|
||||
|
||||
self.body.overlay(body_layout)
|
||||
self.body.overlay(layout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::container;
|
||||
use crate::event::{self, Event};
|
||||
use crate::layout;
|
||||
use crate::overlay;
|
||||
use crate::pane_grid;
|
||||
use crate::{
|
||||
Clipboard, Element, Hasher, Layout, Padding, Point, Rectangle, Size,
|
||||
|
@ -242,4 +243,11 @@ where
|
|||
|
||||
control_status.merge(title_status)
|
||||
}
|
||||
|
||||
pub(crate) fn overlay(
|
||||
&mut self,
|
||||
layout: Layout<'_>,
|
||||
) -> Option<overlay::Element<'_, Message, Renderer>> {
|
||||
self.content.overlay(layout)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ pub struct Scrollable<'a, Message, Renderer: self::Renderer> {
|
|||
scrollbar_margin: u16,
|
||||
scroller_width: u16,
|
||||
content: Column<'a, Message, Renderer>,
|
||||
on_scroll: Option<Box<dyn Fn(f32) -> Message>>,
|
||||
style: Renderer::Style,
|
||||
}
|
||||
|
||||
|
@ -37,6 +38,7 @@ impl<'a, Message, Renderer: self::Renderer> Scrollable<'a, Message, Renderer> {
|
|||
scrollbar_margin: 0,
|
||||
scroller_width: 10,
|
||||
content: Column::new(),
|
||||
on_scroll: None,
|
||||
style: Renderer::Style::default(),
|
||||
}
|
||||
}
|
||||
|
@ -101,12 +103,22 @@ impl<'a, Message, Renderer: self::Renderer> Scrollable<'a, Message, Renderer> {
|
|||
}
|
||||
|
||||
/// Sets the scroller width of the [`Scrollable`] .
|
||||
/// Silently enforces a minimum value of 1.
|
||||
///
|
||||
/// It silently enforces a minimum value of 1.
|
||||
pub fn scroller_width(mut self, scroller_width: u16) -> Self {
|
||||
self.scroller_width = scroller_width.max(1);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets a function to call when the [`Scrollable`] is scrolled.
|
||||
///
|
||||
/// The function takes the new relative offset of the [`Scrollable`]
|
||||
/// (e.g. `0` means top, while `1` means bottom).
|
||||
pub fn on_scroll(mut self, f: impl Fn(f32) -> Message + 'static) -> Self {
|
||||
self.on_scroll = Some(Box::new(f));
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the style of the [`Scrollable`] .
|
||||
pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self {
|
||||
self.style = style.into();
|
||||
|
@ -121,6 +133,24 @@ impl<'a, Message, Renderer: self::Renderer> Scrollable<'a, Message, Renderer> {
|
|||
self.content = self.content.push(child);
|
||||
self
|
||||
}
|
||||
|
||||
fn notify_on_scroll(
|
||||
&self,
|
||||
bounds: Rectangle,
|
||||
content_bounds: Rectangle,
|
||||
messages: &mut Vec<Message>,
|
||||
) {
|
||||
if content_bounds.height <= bounds.height {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(on_scroll) = &self.on_scroll {
|
||||
messages.push(on_scroll(
|
||||
self.state.offset.absolute(bounds, content_bounds)
|
||||
/ (content_bounds.height - bounds.height),
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||
|
@ -228,6 +258,8 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
self.notify_on_scroll(bounds, content_bounds, messages);
|
||||
|
||||
return event::Status::Captured;
|
||||
}
|
||||
Event::Touch(event) => {
|
||||
|
@ -251,6 +283,12 @@ where
|
|||
|
||||
self.state.scroll_box_touched_at =
|
||||
Some(cursor_position);
|
||||
|
||||
self.notify_on_scroll(
|
||||
bounds,
|
||||
content_bounds,
|
||||
messages,
|
||||
);
|
||||
}
|
||||
}
|
||||
touch::Event::FingerLifted { .. }
|
||||
|
@ -290,6 +328,8 @@ where
|
|||
content_bounds,
|
||||
);
|
||||
|
||||
self.notify_on_scroll(bounds, content_bounds, messages);
|
||||
|
||||
return event::Status::Captured;
|
||||
}
|
||||
}
|
||||
|
@ -317,6 +357,12 @@ where
|
|||
self.state.scroller_grabbed_at =
|
||||
Some(scroller_grabbed_at);
|
||||
|
||||
self.notify_on_scroll(
|
||||
bounds,
|
||||
content_bounds,
|
||||
messages,
|
||||
);
|
||||
|
||||
return event::Status::Captured;
|
||||
}
|
||||
}
|
||||
|
@ -418,11 +464,44 @@ where
|
|||
}
|
||||
|
||||
/// The local state of a [`Scrollable`].
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct State {
|
||||
scroller_grabbed_at: Option<f32>,
|
||||
scroll_box_touched_at: Option<Point>,
|
||||
offset: f32,
|
||||
offset: Offset,
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
scroller_grabbed_at: None,
|
||||
scroll_box_touched_at: None,
|
||||
offset: Offset::Absolute(0.0),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The local state of a [`Scrollable`].
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Offset {
|
||||
Absolute(f32),
|
||||
Relative(f32),
|
||||
}
|
||||
|
||||
impl Offset {
|
||||
fn absolute(self, bounds: Rectangle, content_bounds: Rectangle) -> f32 {
|
||||
match self {
|
||||
Self::Absolute(absolute) => {
|
||||
let hidden_content =
|
||||
(content_bounds.height - bounds.height).max(0.0);
|
||||
|
||||
absolute.min(hidden_content)
|
||||
}
|
||||
Self::Relative(percentage) => {
|
||||
((content_bounds.height - bounds.height) * percentage).max(0.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl State {
|
||||
|
@ -443,13 +522,14 @@ impl State {
|
|||
return;
|
||||
}
|
||||
|
||||
self.offset = (self.offset - delta_y)
|
||||
.max(0.0)
|
||||
.min((content_bounds.height - bounds.height) as f32);
|
||||
self.offset = Offset::Absolute(
|
||||
(self.offset.absolute(bounds, content_bounds) - delta_y)
|
||||
.max(0.0)
|
||||
.min((content_bounds.height - bounds.height) as f32),
|
||||
);
|
||||
}
|
||||
|
||||
/// Moves the scroll position to a relative amount, given the bounds of
|
||||
/// the [`Scrollable`] and its contents.
|
||||
/// Scrolls the [`Scrollable`] to a relative amount.
|
||||
///
|
||||
/// `0` represents scrollbar at the top, while `1` represents scrollbar at
|
||||
/// the bottom.
|
||||
|
@ -459,17 +539,29 @@ impl State {
|
|||
bounds: Rectangle,
|
||||
content_bounds: Rectangle,
|
||||
) {
|
||||
self.snap_to(percentage);
|
||||
self.unsnap(bounds, content_bounds);
|
||||
}
|
||||
|
||||
/// Snaps the scroll position to a relative amount.
|
||||
///
|
||||
/// `0` represents scrollbar at the top, while `1` represents scrollbar at
|
||||
/// the bottom.
|
||||
pub fn snap_to(&mut self, percentage: f32) {
|
||||
self.offset = Offset::Relative(percentage.max(0.0).min(1.0));
|
||||
}
|
||||
|
||||
/// Unsnaps the current scroll position, if snapped, given the bounds of the
|
||||
/// [`Scrollable`] and its contents.
|
||||
pub fn unsnap(&mut self, bounds: Rectangle, content_bounds: Rectangle) {
|
||||
self.offset =
|
||||
((content_bounds.height - bounds.height) * percentage).max(0.0);
|
||||
Offset::Absolute(self.offset.absolute(bounds, content_bounds));
|
||||
}
|
||||
|
||||
/// Returns the current scrolling offset of the [`State`], given the bounds
|
||||
/// of the [`Scrollable`] and its contents.
|
||||
pub fn offset(&self, bounds: Rectangle, content_bounds: Rectangle) -> u32 {
|
||||
let hidden_content =
|
||||
(content_bounds.height - bounds.height).max(0.0).round() as u32;
|
||||
|
||||
self.offset.min(hidden_content as f32) as u32
|
||||
self.offset.absolute(bounds, content_bounds) as u32
|
||||
}
|
||||
|
||||
/// Returns whether the scroller is currently grabbed or not.
|
||||
|
|
|
@ -0,0 +1,277 @@
|
|||
//! Show toggle controls using togglers.
|
||||
use std::hash::Hash;
|
||||
|
||||
use crate::{
|
||||
event, layout, mouse, row, text, Align, Clipboard, Element, Event, Hasher,
|
||||
HorizontalAlignment, Layout, Length, Point, Rectangle, Row, Text,
|
||||
VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
/// A toggler widget
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # type Toggler<Message> = iced_native::Toggler<Message, iced_native::renderer::Null>;
|
||||
/// #
|
||||
/// pub enum Message {
|
||||
/// TogglerToggled(bool),
|
||||
/// }
|
||||
///
|
||||
/// let is_active = true;
|
||||
///
|
||||
/// Toggler::new(is_active, String::from("Toggle me!"), |b| Message::TogglerToggled(b));
|
||||
/// ```
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Toggler<Message, Renderer: self::Renderer + text::Renderer> {
|
||||
is_active: bool,
|
||||
on_toggle: Box<dyn Fn(bool) -> Message>,
|
||||
label: Option<String>,
|
||||
width: Length,
|
||||
size: u16,
|
||||
text_size: Option<u16>,
|
||||
text_alignment: HorizontalAlignment,
|
||||
spacing: u16,
|
||||
font: Renderer::Font,
|
||||
style: Renderer::Style,
|
||||
}
|
||||
|
||||
impl<Message, Renderer: self::Renderer + text::Renderer>
|
||||
Toggler<Message, Renderer>
|
||||
{
|
||||
/// Creates a new [`Toggler`].
|
||||
///
|
||||
/// It expects:
|
||||
/// * a boolean describing whether the [`Toggler`] is checked or not
|
||||
/// * An optional label for the [`Toggler`]
|
||||
/// * a function that will be called when the [`Toggler`] is toggled. It
|
||||
/// will receive the new state of the [`Toggler`] and must produce a
|
||||
/// `Message`.
|
||||
pub fn new<F>(
|
||||
is_active: bool,
|
||||
label: impl Into<Option<String>>,
|
||||
f: F,
|
||||
) -> Self
|
||||
where
|
||||
F: 'static + Fn(bool) -> Message,
|
||||
{
|
||||
Toggler {
|
||||
is_active,
|
||||
on_toggle: Box::new(f),
|
||||
label: label.into(),
|
||||
width: Length::Fill,
|
||||
size: <Renderer as self::Renderer>::DEFAULT_SIZE,
|
||||
text_size: None,
|
||||
text_alignment: HorizontalAlignment::Left,
|
||||
spacing: 0,
|
||||
font: Renderer::Font::default(),
|
||||
style: Renderer::Style::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the size of the [`Toggler`].
|
||||
pub fn size(mut self, size: u16) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the width of the [`Toggler`].
|
||||
pub fn width(mut self, width: Length) -> Self {
|
||||
self.width = width;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the text size o the [`Toggler`].
|
||||
pub fn text_size(mut self, text_size: u16) -> Self {
|
||||
self.text_size = Some(text_size);
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the horizontal alignment of the text of the [`Toggler`]
|
||||
pub fn text_alignment(mut self, alignment: HorizontalAlignment) -> Self {
|
||||
self.text_alignment = alignment;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the spacing between the [`Toggler`] and the text.
|
||||
pub fn spacing(mut self, spacing: u16) -> Self {
|
||||
self.spacing = spacing;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the [`Font`] of the text of the [`Toggler`]
|
||||
pub fn font(mut self, font: Renderer::Font) -> Self {
|
||||
self.font = font;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the style of the [`Toggler`].
|
||||
pub fn style(mut self, style: impl Into<Renderer::Style>) -> Self {
|
||||
self.style = style.into();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<Message, Renderer> Widget<Message, Renderer> for Toggler<Message, Renderer>
|
||||
where
|
||||
Renderer: self::Renderer + text::Renderer + row::Renderer,
|
||||
{
|
||||
fn width(&self) -> Length {
|
||||
self.width
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
Length::Shrink
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let mut row = Row::<(), Renderer>::new()
|
||||
.width(self.width)
|
||||
.spacing(self.spacing)
|
||||
.align_items(Align::Center);
|
||||
|
||||
if let Some(label) = &self.label {
|
||||
row = row.push(
|
||||
Text::new(label)
|
||||
.horizontal_alignment(self.text_alignment)
|
||||
.font(self.font)
|
||||
.width(self.width)
|
||||
.size(self.text_size.unwrap_or(renderer.default_size())),
|
||||
);
|
||||
}
|
||||
|
||||
row = row.push(
|
||||
Row::new()
|
||||
.width(Length::Units(2 * self.size))
|
||||
.height(Length::Units(self.size)),
|
||||
);
|
||||
|
||||
row.layout(renderer, limits)
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_renderer: &Renderer,
|
||||
_clipboard: &mut dyn Clipboard,
|
||||
messages: &mut Vec<Message>,
|
||||
) -> event::Status {
|
||||
match event {
|
||||
Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)) => {
|
||||
let mouse_over = layout.bounds().contains(cursor_position);
|
||||
|
||||
if mouse_over {
|
||||
messages.push((self.on_toggle)(!self.is_active));
|
||||
|
||||
event::Status::Captured
|
||||
} else {
|
||||
event::Status::Ignored
|
||||
}
|
||||
}
|
||||
_ => event::Status::Ignored,
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
renderer: &mut Renderer,
|
||||
defaults: &Renderer::Defaults,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
) -> Renderer::Output {
|
||||
let bounds = layout.bounds();
|
||||
let mut children = layout.children();
|
||||
|
||||
let label = match &self.label {
|
||||
Some(label) => {
|
||||
let label_layout = children.next().unwrap();
|
||||
|
||||
Some(text::Renderer::draw(
|
||||
renderer,
|
||||
defaults,
|
||||
label_layout.bounds(),
|
||||
&label,
|
||||
self.text_size.unwrap_or(renderer.default_size()),
|
||||
self.font,
|
||||
None,
|
||||
self.text_alignment,
|
||||
VerticalAlignment::Center,
|
||||
))
|
||||
}
|
||||
|
||||
None => None,
|
||||
};
|
||||
|
||||
let toggler_layout = children.next().unwrap();
|
||||
let toggler_bounds = toggler_layout.bounds();
|
||||
|
||||
let is_mouse_over = bounds.contains(cursor_position);
|
||||
|
||||
self::Renderer::draw(
|
||||
renderer,
|
||||
toggler_bounds,
|
||||
self.is_active,
|
||||
is_mouse_over,
|
||||
label,
|
||||
&self.style,
|
||||
)
|
||||
}
|
||||
|
||||
fn hash_layout(&self, state: &mut Hasher) {
|
||||
struct Marker;
|
||||
std::any::TypeId::of::<Marker>().hash(state);
|
||||
|
||||
self.label.hash(state)
|
||||
}
|
||||
}
|
||||
|
||||
/// The renderer of a [`Toggler`].
|
||||
///
|
||||
/// Your [renderer] will need to implement this trait before being
|
||||
/// able to use a [`Toggler`] in your user interface.
|
||||
///
|
||||
/// [renderer]: ../../renderer/index.html
|
||||
pub trait Renderer: crate::Renderer {
|
||||
/// The style supported by this renderer.
|
||||
type Style: Default;
|
||||
|
||||
/// The default size of a [`Toggler`].
|
||||
const DEFAULT_SIZE: u16;
|
||||
|
||||
/// Draws a [`Toggler`].
|
||||
///
|
||||
/// It receives:
|
||||
/// * the bounds of the [`Toggler`]
|
||||
/// * whether the [`Toggler`] is activated or not
|
||||
/// * whether the mouse is over the [`Toggler`] or not
|
||||
/// * the drawn label of the [`Toggler`]
|
||||
/// * the style of the [`Toggler`]
|
||||
fn draw(
|
||||
&mut self,
|
||||
bounds: Rectangle,
|
||||
is_active: bool,
|
||||
is_mouse_over: bool,
|
||||
label: Option<Self::Output>,
|
||||
style: &Self::Style,
|
||||
) -> Self::Output;
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> From<Toggler<Message, Renderer>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
Renderer: 'a + self::Renderer + text::Renderer + row::Renderer,
|
||||
Message: 'a,
|
||||
{
|
||||
fn from(
|
||||
toggler: Toggler<Message, Renderer>,
|
||||
) -> Element<'a, Message, Renderer> {
|
||||
Element::new(toggler)
|
||||
}
|
||||
}
|
|
@ -39,15 +39,15 @@ use crate::{
|
|||
/// to listen to time.
|
||||
/// - [`todos`], a todos tracker inspired by [TodoMVC].
|
||||
///
|
||||
/// [The repository has a bunch of examples]: https://github.com/hecrj/iced/tree/0.2/examples
|
||||
/// [`clock`]: https://github.com/hecrj/iced/tree/0.2/examples/clock
|
||||
/// [`download_progress`]: https://github.com/hecrj/iced/tree/0.2/examples/download_progress
|
||||
/// [`events`]: https://github.com/hecrj/iced/tree/0.2/examples/events
|
||||
/// [`game_of_life`]: https://github.com/hecrj/iced/tree/0.2/examples/game_of_life
|
||||
/// [`pokedex`]: https://github.com/hecrj/iced/tree/0.2/examples/pokedex
|
||||
/// [`solar_system`]: https://github.com/hecrj/iced/tree/0.2/examples/solar_system
|
||||
/// [`stopwatch`]: https://github.com/hecrj/iced/tree/0.2/examples/stopwatch
|
||||
/// [`todos`]: https://github.com/hecrj/iced/tree/0.2/examples/todos
|
||||
/// [The repository has a bunch of examples]: https://github.com/hecrj/iced/tree/0.3/examples
|
||||
/// [`clock`]: https://github.com/hecrj/iced/tree/0.3/examples/clock
|
||||
/// [`download_progress`]: https://github.com/hecrj/iced/tree/0.3/examples/download_progress
|
||||
/// [`events`]: https://github.com/hecrj/iced/tree/0.3/examples/events
|
||||
/// [`game_of_life`]: https://github.com/hecrj/iced/tree/0.3/examples/game_of_life
|
||||
/// [`pokedex`]: https://github.com/hecrj/iced/tree/0.3/examples/pokedex
|
||||
/// [`solar_system`]: https://github.com/hecrj/iced/tree/0.3/examples/solar_system
|
||||
/// [`stopwatch`]: https://github.com/hecrj/iced/tree/0.3/examples/stopwatch
|
||||
/// [`todos`]: https://github.com/hecrj/iced/tree/0.3/examples/todos
|
||||
/// [`Sandbox`]: crate::Sandbox
|
||||
/// [`Canvas`]: crate::widget::Canvas
|
||||
/// [PokéAPI]: https://pokeapi.co/
|
||||
|
|
|
@ -36,19 +36,19 @@ use crate::{
|
|||
/// - [`tour`], a simple UI tour that can run both on native platforms and the
|
||||
/// web!
|
||||
///
|
||||
/// [The repository has a bunch of examples]: https://github.com/hecrj/iced/tree/0.2/examples
|
||||
/// [`bezier_tool`]: https://github.com/hecrj/iced/tree/0.2/examples/bezier_tool
|
||||
/// [`counter`]: https://github.com/hecrj/iced/tree/0.2/examples/counter
|
||||
/// [`custom_widget`]: https://github.com/hecrj/iced/tree/0.2/examples/custom_widget
|
||||
/// [`geometry`]: https://github.com/hecrj/iced/tree/0.2/examples/geometry
|
||||
/// [`pane_grid`]: https://github.com/hecrj/iced/tree/0.2/examples/pane_grid
|
||||
/// [`progress_bar`]: https://github.com/hecrj/iced/tree/0.2/examples/progress_bar
|
||||
/// [`styling`]: https://github.com/hecrj/iced/tree/0.2/examples/styling
|
||||
/// [`svg`]: https://github.com/hecrj/iced/tree/0.2/examples/svg
|
||||
/// [`tour`]: https://github.com/hecrj/iced/tree/0.2/examples/tour
|
||||
/// [The repository has a bunch of examples]: https://github.com/hecrj/iced/tree/0.3/examples
|
||||
/// [`bezier_tool`]: https://github.com/hecrj/iced/tree/0.3/examples/bezier_tool
|
||||
/// [`counter`]: https://github.com/hecrj/iced/tree/0.3/examples/counter
|
||||
/// [`custom_widget`]: https://github.com/hecrj/iced/tree/0.3/examples/custom_widget
|
||||
/// [`geometry`]: https://github.com/hecrj/iced/tree/0.3/examples/geometry
|
||||
/// [`pane_grid`]: https://github.com/hecrj/iced/tree/0.3/examples/pane_grid
|
||||
/// [`progress_bar`]: https://github.com/hecrj/iced/tree/0.3/examples/progress_bar
|
||||
/// [`styling`]: https://github.com/hecrj/iced/tree/0.3/examples/styling
|
||||
/// [`svg`]: https://github.com/hecrj/iced/tree/0.3/examples/svg
|
||||
/// [`tour`]: https://github.com/hecrj/iced/tree/0.3/examples/tour
|
||||
/// [`Canvas widget`]: crate::widget::Canvas
|
||||
/// [the overview]: index.html#overview
|
||||
/// [`iced_wgpu`]: https://github.com/hecrj/iced/tree/0.2/wgpu
|
||||
/// [`iced_wgpu`]: https://github.com/hecrj/iced/tree/0.3/wgpu
|
||||
/// [`Svg` widget]: crate::widget::Svg
|
||||
/// [Ghostscript Tiger]: https://commons.wikimedia.org/wiki/File:Ghostscript_Tiger.svg
|
||||
///
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
mod platform {
|
||||
pub use crate::renderer::widget::{
|
||||
button, checkbox, container, pane_grid, pick_list, progress_bar, radio,
|
||||
rule, scrollable, slider, text_input, tooltip, Column, Row, Space,
|
||||
Text,
|
||||
rule, scrollable, slider, text_input, toggler, tooltip, Column, Row,
|
||||
Space, Text,
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "canvas", feature = "glow_canvas"))]
|
||||
|
@ -53,7 +53,7 @@ mod platform {
|
|||
button::Button, checkbox::Checkbox, container::Container, image::Image,
|
||||
pane_grid::PaneGrid, pick_list::PickList, progress_bar::ProgressBar,
|
||||
radio::Radio, rule::Rule, scrollable::Scrollable, slider::Slider,
|
||||
svg::Svg, text_input::TextInput, tooltip::Tooltip,
|
||||
svg::Svg, text_input::TextInput, toggler::Toggler, tooltip::Tooltip,
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "canvas", feature = "glow_canvas"))]
|
||||
|
|
|
@ -18,3 +18,4 @@ pub mod rule;
|
|||
pub mod scrollable;
|
||||
pub mod slider;
|
||||
pub mod text_input;
|
||||
pub mod toggler;
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
//! Show toggle controls using togglers.
|
||||
use iced_core::Color;
|
||||
|
||||
/// The appearance of a toggler.
|
||||
#[derive(Debug)]
|
||||
pub struct Style {
|
||||
pub background: Color,
|
||||
pub background_border: Option<Color>,
|
||||
pub foreground: Color,
|
||||
pub foreground_border: Option<Color>,
|
||||
}
|
||||
|
||||
/// A set of rules that dictate the style of a toggler.
|
||||
pub trait StyleSheet {
|
||||
fn active(&self, is_active: bool) -> Style;
|
||||
|
||||
fn hovered(&self, is_active: bool) -> Style;
|
||||
}
|
||||
|
||||
struct Default;
|
||||
|
||||
impl StyleSheet for Default {
|
||||
fn active(&self, is_active: bool) -> Style {
|
||||
Style {
|
||||
background: if is_active {
|
||||
Color::from_rgb(0.0, 1.0, 0.0)
|
||||
} else {
|
||||
Color::from_rgb(0.7, 0.7, 0.7)
|
||||
},
|
||||
background_border: None,
|
||||
foreground: Color::WHITE,
|
||||
foreground_border: None,
|
||||
}
|
||||
}
|
||||
|
||||
fn hovered(&self, is_active: bool) -> Style {
|
||||
Style {
|
||||
foreground: Color::from_rgb(0.95, 0.95, 0.95),
|
||||
..self.active(is_active)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::default::Default for Box<dyn StyleSheet> {
|
||||
fn default() -> Self {
|
||||
Box::new(Default)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<T> for Box<dyn StyleSheet>
|
||||
where
|
||||
T: 'static + StyleSheet,
|
||||
{
|
||||
fn from(style: T) -> Self {
|
||||
Box::new(style)
|
||||
}
|
||||
}
|
|
@ -14,6 +14,9 @@ pub enum Rule {
|
|||
|
||||
/// Spacing between elements
|
||||
Spacing(u16),
|
||||
|
||||
/// Toggler input for a specific size
|
||||
Toggler(u16),
|
||||
}
|
||||
|
||||
impl Rule {
|
||||
|
@ -23,6 +26,7 @@ impl Rule {
|
|||
Rule::Column => String::from("c"),
|
||||
Rule::Row => String::from("r"),
|
||||
Rule::Spacing(spacing) => format!("s-{}", spacing),
|
||||
Rule::Toggler(size) => format!("toggler-{}", size),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -55,6 +59,46 @@ impl Rule {
|
|||
class
|
||||
)
|
||||
.into_bump_str(),
|
||||
Rule::Toggler(size) => bumpalo::format!(
|
||||
in bump,
|
||||
".toggler-{} {{ display: flex; cursor: pointer; justify-content: space-between; }} \
|
||||
.toggler-{} input {{ display:none; }} \
|
||||
.toggler-{} span {{ background-color: #b1b1b1; position: relative; display: inline-flex; width:{}px; height: {}px; border-radius: {}px;}} \
|
||||
.toggler-{} span > span {{ background-color: #FFFFFF; width: {}px; height: {}px; border-radius: 50%; top: 1px; left: 1px;}} \
|
||||
.toggler-{}:hover span > span {{ background-color: #f1f1f1 !important; }} \
|
||||
.toggler-{} input:checked + span {{ background-color: #00FF00; }} \
|
||||
.toggler-{} input:checked + span > span {{ -webkit-transform: translateX({}px); -ms-transform:translateX({}px); transform: translateX({}px); }}
|
||||
",
|
||||
// toggler
|
||||
size,
|
||||
|
||||
// toggler input
|
||||
size,
|
||||
|
||||
// toggler span
|
||||
size,
|
||||
size*2,
|
||||
size,
|
||||
size,
|
||||
|
||||
// toggler span > span
|
||||
size,
|
||||
size-2,
|
||||
size-2,
|
||||
|
||||
// toggler: hover + span > span
|
||||
size,
|
||||
|
||||
// toggler input:checked + span
|
||||
size,
|
||||
|
||||
// toggler input:checked + span > span
|
||||
size,
|
||||
size,
|
||||
size,
|
||||
size
|
||||
)
|
||||
.into_bump_str(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@
|
|||
//!
|
||||
//! [`wasm-pack`]: https://github.com/rustwasm/wasm-pack
|
||||
//! [`wasm-bindgen`]: https://github.com/rustwasm/wasm-bindgen
|
||||
//! [`tour` example]: https://github.com/hecrj/iced/tree/0.2/examples/tour
|
||||
//! [`tour` example]: https://github.com/hecrj/iced/tree/0.3/examples/tour
|
||||
#![deny(missing_docs)]
|
||||
#![deny(missing_debug_implementations)]
|
||||
#![deny(unused_results)]
|
||||
|
|
|
@ -24,6 +24,7 @@ pub mod radio;
|
|||
pub mod scrollable;
|
||||
pub mod slider;
|
||||
pub mod text_input;
|
||||
pub mod toggler;
|
||||
|
||||
mod column;
|
||||
mod row;
|
||||
|
@ -40,6 +41,8 @@ pub use slider::Slider;
|
|||
pub use text::Text;
|
||||
#[doc(no_inline)]
|
||||
pub use text_input::TextInput;
|
||||
#[doc(no_inline)]
|
||||
pub use toggler::Toggler;
|
||||
|
||||
pub use checkbox::Checkbox;
|
||||
pub use column::Column;
|
||||
|
|
|
@ -20,6 +20,26 @@ use dodrio::bumpalo;
|
|||
/// let button = Button::new(&mut state, Text::new("Press me!"))
|
||||
/// .on_press(Message::ButtonPressed);
|
||||
/// ```
|
||||
///
|
||||
/// If a [`Button::on_press`] handler is not set, the resulting [`Button`] will
|
||||
/// be disabled:
|
||||
///
|
||||
/// ```
|
||||
/// # use iced_web::{button, Button, Text};
|
||||
/// #
|
||||
/// #[derive(Clone)]
|
||||
/// enum Message {
|
||||
/// ButtonPressed,
|
||||
/// }
|
||||
///
|
||||
/// fn disabled_button(state: &mut button::State) -> Button<'_, Message> {
|
||||
/// Button::new(state, Text::new("I'm disabled!"))
|
||||
/// }
|
||||
///
|
||||
/// fn enabled_button(state: &mut button::State) -> Button<'_, Message> {
|
||||
/// disabled_button(state).on_press(Message::ButtonPressed)
|
||||
/// }
|
||||
/// ```
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Button<'a, Message> {
|
||||
content: Element<'a, Message>,
|
||||
|
@ -90,6 +110,7 @@ impl<'a, Message> Button<'a, Message> {
|
|||
}
|
||||
|
||||
/// Sets the message that will be produced when the [`Button`] is pressed.
|
||||
/// If on_press isn't set, button will be disabled.
|
||||
pub fn on_press(mut self, msg: Message) -> Self {
|
||||
self.on_press = Some(msg);
|
||||
self
|
||||
|
@ -153,6 +174,8 @@ where
|
|||
node = node.on("click", move |_root, _vdom, _event| {
|
||||
event_bus.publish(on_press.clone());
|
||||
});
|
||||
} else {
|
||||
node = node.attr("disabled", "");
|
||||
}
|
||||
|
||||
node.finish()
|
||||
|
|
|
@ -0,0 +1,171 @@
|
|||
//! Show toggle controls using togglers.
|
||||
use crate::{css, Bus, Css, Element, Length, Widget};
|
||||
|
||||
pub use iced_style::toggler::{Style, StyleSheet};
|
||||
|
||||
use dodrio::bumpalo;
|
||||
use std::rc::Rc;
|
||||
|
||||
/// A toggler that can be toggled.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # use iced_web::Toggler;
|
||||
///
|
||||
/// pub enum Message {
|
||||
/// TogglerToggled(bool),
|
||||
/// }
|
||||
///
|
||||
/// let is_active = true;
|
||||
///
|
||||
/// Toggler::new(is_active, String::from("Toggle me!"), Message::TogglerToggled);
|
||||
/// ```
|
||||
///
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Toggler<Message> {
|
||||
is_active: bool,
|
||||
on_toggle: Rc<dyn Fn(bool) -> Message>,
|
||||
label: Option<String>,
|
||||
id: Option<String>,
|
||||
width: Length,
|
||||
style: Box<dyn StyleSheet>,
|
||||
}
|
||||
|
||||
impl<Message> Toggler<Message> {
|
||||
/// Creates a new [`Toggler`].
|
||||
///
|
||||
/// It expects:
|
||||
/// * a boolean describing whether the [`Toggler`] is active or not
|
||||
/// * An optional label for the [`Toggler`]
|
||||
/// * a function that will be called when the [`Toggler`] is toggled. It
|
||||
/// will receive the new state of the [`Toggler`] and must produce a
|
||||
/// `Message`.
|
||||
///
|
||||
/// [`Toggler`]: struct.Toggler.html
|
||||
pub fn new<F>(
|
||||
is_active: bool,
|
||||
label: impl Into<Option<String>>,
|
||||
f: F,
|
||||
) -> Self
|
||||
where
|
||||
F: 'static + Fn(bool) -> Message,
|
||||
{
|
||||
Toggler {
|
||||
is_active,
|
||||
on_toggle: Rc::new(f),
|
||||
label: label.into(),
|
||||
id: None,
|
||||
width: Length::Shrink,
|
||||
style: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the width of the [`Toggler`].
|
||||
///
|
||||
/// [`Toggler`]: struct.Toggler.html
|
||||
pub fn width(mut self, width: Length) -> Self {
|
||||
self.width = width;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the style of the [`Toggler`].
|
||||
///
|
||||
/// [`Toggler`]: struct.Toggler.html
|
||||
pub fn style(mut self, style: impl Into<Box<dyn StyleSheet>>) -> Self {
|
||||
self.style = style.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the id of the [`Toggler`].
|
||||
///
|
||||
/// [`Toggler`]: struct.Toggler.html
|
||||
pub fn id(mut self, id: impl Into<String>) -> Self {
|
||||
self.id = Some(id.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<Message> Widget<Message> for Toggler<Message>
|
||||
where
|
||||
Message: 'static,
|
||||
{
|
||||
fn node<'b>(
|
||||
&self,
|
||||
bump: &'b bumpalo::Bump,
|
||||
bus: &Bus<Message>,
|
||||
style_sheet: &mut Css<'b>,
|
||||
) -> dodrio::Node<'b> {
|
||||
use dodrio::builder::*;
|
||||
use dodrio::bumpalo::collections::String;
|
||||
|
||||
let toggler_label = &self
|
||||
.label
|
||||
.as_ref()
|
||||
.map(|label| String::from_str_in(&label, bump).into_bump_str());
|
||||
|
||||
let event_bus = bus.clone();
|
||||
let on_toggle = self.on_toggle.clone();
|
||||
let is_active = self.is_active;
|
||||
|
||||
let row_class = style_sheet.insert(bump, css::Rule::Row);
|
||||
let toggler_class = style_sheet.insert(bump, css::Rule::Toggler(16));
|
||||
|
||||
let (label, input) = if let Some(id) = &self.id {
|
||||
let id = String::from_str_in(id, bump).into_bump_str();
|
||||
|
||||
(label(bump).attr("for", id), input(bump).attr("id", id))
|
||||
} else {
|
||||
(label(bump), input(bump))
|
||||
};
|
||||
|
||||
let checkbox = input
|
||||
.attr("type", "checkbox")
|
||||
.bool_attr("checked", self.is_active)
|
||||
.on("click", move |_root, vdom, _event| {
|
||||
let msg = on_toggle(!is_active);
|
||||
event_bus.publish(msg);
|
||||
|
||||
vdom.schedule_render();
|
||||
})
|
||||
.finish();
|
||||
|
||||
let toggler = span(bump).children(vec![span(bump).finish()]).finish();
|
||||
|
||||
label
|
||||
.attr(
|
||||
"class",
|
||||
bumpalo::format!(in bump, "{} {}", row_class, toggler_class)
|
||||
.into_bump_str(),
|
||||
)
|
||||
.attr(
|
||||
"style",
|
||||
bumpalo::format!(in bump, "width: {}; align-items: center", css::length(self.width))
|
||||
.into_bump_str()
|
||||
)
|
||||
.children(
|
||||
if let Some(label) = toggler_label {
|
||||
vec![
|
||||
text(label),
|
||||
checkbox,
|
||||
toggler,
|
||||
]
|
||||
} else {
|
||||
vec![
|
||||
checkbox,
|
||||
toggler,
|
||||
]
|
||||
}
|
||||
)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message> From<Toggler<Message>> for Element<'a, Message>
|
||||
where
|
||||
Message: 'static,
|
||||
{
|
||||
fn from(toggler: Toggler<Message>) -> Element<'a, Message> {
|
||||
Element::new(toggler)
|
||||
}
|
||||
}
|
|
@ -26,8 +26,8 @@ qr_code = ["iced_graphics/qr_code"]
|
|||
default_system_font = ["iced_graphics/font-source"]
|
||||
|
||||
[dependencies]
|
||||
wgpu = "0.8"
|
||||
wgpu_glyph = "0.12"
|
||||
wgpu = "0.9"
|
||||
wgpu_glyph = "0.13"
|
||||
glyph_brush = "0.7"
|
||||
raw-window-handle = "0.3"
|
||||
log = "0.4"
|
||||
|
|
|
@ -165,33 +165,13 @@ impl Pipeline {
|
|||
wgpu::VertexBufferLayout {
|
||||
array_stride: mem::size_of::<Instance>() as u64,
|
||||
step_mode: wgpu::InputStepMode::Instance,
|
||||
attributes: &[
|
||||
wgpu::VertexAttribute {
|
||||
shader_location: 1,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
offset: 0,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
shader_location: 2,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
offset: 4 * 2,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
shader_location: 3,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
offset: 4 * 4,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
shader_location: 4,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
offset: 4 * 6,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
shader_location: 5,
|
||||
format: wgpu::VertexFormat::Sint32,
|
||||
offset: 4 * 8,
|
||||
},
|
||||
],
|
||||
attributes: &wgpu::vertex_attr_array!(
|
||||
1 => Float32x2,
|
||||
2 => Float32x2,
|
||||
3 => Float32x2,
|
||||
4 => Float32x2,
|
||||
5 => Sint32,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -87,38 +87,14 @@ impl Pipeline {
|
|||
wgpu::VertexBufferLayout {
|
||||
array_stride: mem::size_of::<layer::Quad>() as u64,
|
||||
step_mode: wgpu::InputStepMode::Instance,
|
||||
attributes: &[
|
||||
wgpu::VertexAttribute {
|
||||
shader_location: 1,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
offset: 0,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
shader_location: 2,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
offset: 4 * 2,
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
shader_location: 3,
|
||||
format: wgpu::VertexFormat::Float32x4,
|
||||
offset: 4 * (2 + 2),
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
shader_location: 4,
|
||||
format: wgpu::VertexFormat::Float32x4,
|
||||
offset: 4 * (2 + 2 + 4),
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
shader_location: 5,
|
||||
format: wgpu::VertexFormat::Float32,
|
||||
offset: 4 * (2 + 2 + 4 + 4),
|
||||
},
|
||||
wgpu::VertexAttribute {
|
||||
shader_location: 6,
|
||||
format: wgpu::VertexFormat::Float32,
|
||||
offset: 4 * (2 + 2 + 4 + 4 + 1),
|
||||
},
|
||||
],
|
||||
attributes: &wgpu::vertex_attr_array!(
|
||||
1 => Float32x2,
|
||||
2 => Float32x2,
|
||||
3 => Float32x4,
|
||||
4 => Float32x4,
|
||||
5 => Float32,
|
||||
6 => Float32,
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
@ -150,20 +150,12 @@ impl Pipeline {
|
|||
buffers: &[wgpu::VertexBufferLayout {
|
||||
array_stride: mem::size_of::<Vertex2D>() as u64,
|
||||
step_mode: wgpu::InputStepMode::Vertex,
|
||||
attributes: &[
|
||||
attributes: &wgpu::vertex_attr_array!(
|
||||
// Position
|
||||
wgpu::VertexAttribute {
|
||||
shader_location: 0,
|
||||
format: wgpu::VertexFormat::Float32x2,
|
||||
offset: 0,
|
||||
},
|
||||
0 => Float32x2,
|
||||
// Color
|
||||
wgpu::VertexAttribute {
|
||||
shader_location: 1,
|
||||
format: wgpu::VertexFormat::Float32x4,
|
||||
offset: 4 * 2,
|
||||
},
|
||||
],
|
||||
1 => Float32x4,
|
||||
),
|
||||
}],
|
||||
},
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
|
|
|
@ -20,6 +20,7 @@ pub mod rule;
|
|||
pub mod scrollable;
|
||||
pub mod slider;
|
||||
pub mod text_input;
|
||||
pub mod toggler;
|
||||
pub mod tooltip;
|
||||
|
||||
#[doc(no_inline)]
|
||||
|
@ -45,6 +46,8 @@ pub use slider::Slider;
|
|||
#[doc(no_inline)]
|
||||
pub use text_input::TextInput;
|
||||
#[doc(no_inline)]
|
||||
pub use toggler::Toggler;
|
||||
#[doc(no_inline)]
|
||||
pub use tooltip::Tooltip;
|
||||
|
||||
#[cfg(feature = "canvas")]
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
//! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing,
|
||||
//! drag and drop, and hotkey support.
|
||||
//!
|
||||
//! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.2/examples/pane_grid
|
||||
//! [`pane_grid` example]: https://github.com/hecrj/iced/tree/0.3/examples/pane_grid
|
||||
use crate::Renderer;
|
||||
|
||||
pub use iced_graphics::pane_grid::{
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
//! Show toggle controls using togglers.
|
||||
use crate::Renderer;
|
||||
|
||||
pub use iced_graphics::toggler::{Style, StyleSheet};
|
||||
|
||||
/// A toggler that can be toggled
|
||||
///
|
||||
/// This is an alias of an `iced_native` toggler with an `iced_wgpu::Renderer`.
|
||||
pub type Toggler<Message> = iced_native::Toggler<Message, Renderer>;
|
|
@ -14,11 +14,15 @@ categories = ["gui"]
|
|||
debug = ["iced_native/debug"]
|
||||
|
||||
[dependencies]
|
||||
winit = "0.24"
|
||||
window_clipboard = "0.2"
|
||||
log = "0.4"
|
||||
thiserror = "1.0"
|
||||
|
||||
[dependencies.winit]
|
||||
version = "0.25"
|
||||
git = "https://github.com/iced-rs/winit"
|
||||
rev = "9c358959ed99736566d50a511b03d2fed3aac2ae"
|
||||
|
||||
[dependencies.iced_native]
|
||||
version = "0.4"
|
||||
path = "../native"
|
||||
|
|
|
@ -310,6 +310,16 @@ async fn run_instance<A, E, C>(
|
|||
|
||||
window.request_redraw();
|
||||
}
|
||||
event::Event::PlatformSpecific(event::PlatformSpecific::MacOS(
|
||||
event::MacOS::ReceivedUrl(url),
|
||||
)) => {
|
||||
use iced_native::event;
|
||||
events.push(iced_native::Event::PlatformSpecific(
|
||||
event::PlatformSpecific::MacOS(event::MacOS::ReceivedUrl(
|
||||
url,
|
||||
)),
|
||||
));
|
||||
}
|
||||
event::Event::UserEvent(message) => {
|
||||
messages.push(message);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue