Merge branch 'master' of https://github.com/hecrj/iced into wgpu_outdatedframe
This commit is contained in:
commit
e822f654e4
219 changed files with 6266 additions and 1761 deletions
|
|
@ -118,7 +118,7 @@ cargo run --package <example>
|
|||
[Ghostscript Tiger]: https://commons.wikimedia.org/wiki/File:Ghostscript_Tiger.svg
|
||||
|
||||
## [Coffee]
|
||||
Since [Iced was born in May], it has been powering the user interfaces in
|
||||
Since [Iced was born in May 2019], it has been powering the user interfaces in
|
||||
[Coffee], an experimental 2D game engine.
|
||||
|
||||
|
||||
|
|
@ -128,6 +128,6 @@ Since [Iced was born in May], it has been powering the user interfaces in
|
|||
</a>
|
||||
</div>
|
||||
|
||||
[Iced was born in May]: https://github.com/hecrj/coffee/pull/35
|
||||
[Iced was born in May 2019]: https://github.com/hecrj/coffee/pull/35
|
||||
[`ui` module]: https://docs.rs/coffee/0.3.2/coffee/ui/index.html
|
||||
[Coffee]: https://github.com/hecrj/coffee
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use iced::{
|
||||
canvas::{self, Cache, Canvas, Cursor, Geometry, LineCap, Path, Stroke},
|
||||
executor, time, Application, Color, Command, Container, Element, Length,
|
||||
Point, Rectangle, Settings, Subscription, Vector,
|
||||
executor, time, Application, Clipboard, Color, Command, Container, Element,
|
||||
Length, Point, Rectangle, Settings, Subscription, Vector,
|
||||
};
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
|
|
@ -40,7 +40,11 @@ impl Application for Clock {
|
|||
String::from("Clock - Iced")
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
fn update(
|
||||
&mut self,
|
||||
message: Message,
|
||||
_clipboard: &mut Clipboard,
|
||||
) -> Command<Message> {
|
||||
match message {
|
||||
Message::Tick(local_time) => {
|
||||
let now = local_time;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,12 @@
|
|||
[package]
|
||||
name = "download_progress"
|
||||
version = "0.1.0"
|
||||
authors = ["Songtronix <contact@songtronix.com>"]
|
||||
authors = ["Songtronix <contact@songtronix.com>", "Folyd <lyshuhow@gmail.com>"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
iced = { path = "../..", features = ["tokio_old"] }
|
||||
iced = { path = "../..", features = ["tokio"] }
|
||||
iced_native = { path = "../../native" }
|
||||
iced_futures = { path = "../../futures" }
|
||||
reqwest = "0.10"
|
||||
reqwest = "0.11"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
## Download progress
|
||||
|
||||
A basic application that asynchronously downloads a dummy file of 100 MB and tracks the download progress.
|
||||
A basic application that asynchronously downloads multiple dummy files of 100 MB and tracks the download progress.
|
||||
|
||||
The example implements a custom `Subscription` in the __[`download`](src/download.rs)__ module. This subscription downloads and produces messages that can be used to keep track of its progress.
|
||||
|
||||
|
|
|
|||
|
|
@ -1,37 +1,46 @@
|
|||
use iced_futures::futures;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
// Just a little utility function
|
||||
pub fn file<T: ToString>(url: T) -> iced::Subscription<Progress> {
|
||||
pub fn file<I: 'static + Hash + Copy + Send, T: ToString>(
|
||||
id: I,
|
||||
url: T,
|
||||
) -> iced::Subscription<(I, Progress)> {
|
||||
iced::Subscription::from_recipe(Download {
|
||||
id,
|
||||
url: url.to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
pub struct Download {
|
||||
pub struct Download<I> {
|
||||
id: I,
|
||||
url: String,
|
||||
}
|
||||
|
||||
// Make sure iced can use our download stream
|
||||
impl<H, I> iced_native::subscription::Recipe<H, I> for Download
|
||||
impl<H, I, T> iced_native::subscription::Recipe<H, I> for Download<T>
|
||||
where
|
||||
H: std::hash::Hasher,
|
||||
T: 'static + Hash + Copy + Send,
|
||||
H: Hasher,
|
||||
{
|
||||
type Output = Progress;
|
||||
type Output = (T, Progress);
|
||||
|
||||
fn hash(&self, state: &mut H) {
|
||||
use std::hash::Hash;
|
||||
struct Marker;
|
||||
std::any::TypeId::of::<Marker>().hash(state);
|
||||
|
||||
std::any::TypeId::of::<Self>().hash(state);
|
||||
self.url.hash(state);
|
||||
self.id.hash(state);
|
||||
}
|
||||
|
||||
fn stream(
|
||||
self: Box<Self>,
|
||||
_input: futures::stream::BoxStream<'static, I>,
|
||||
) -> futures::stream::BoxStream<'static, Self::Output> {
|
||||
let id = self.id;
|
||||
|
||||
Box::pin(futures::stream::unfold(
|
||||
State::Ready(self.url),
|
||||
|state| async move {
|
||||
move |state| async move {
|
||||
match state {
|
||||
State::Ready(url) => {
|
||||
let response = reqwest::get(&url).await;
|
||||
|
|
@ -40,7 +49,7 @@ where
|
|||
Ok(response) => {
|
||||
if let Some(total) = response.content_length() {
|
||||
Some((
|
||||
Progress::Started,
|
||||
(id, Progress::Started),
|
||||
State::Downloading {
|
||||
response,
|
||||
total,
|
||||
|
|
@ -48,11 +57,14 @@ where
|
|||
},
|
||||
))
|
||||
} else {
|
||||
Some((Progress::Errored, State::Finished))
|
||||
Some((
|
||||
(id, Progress::Errored),
|
||||
State::Finished,
|
||||
))
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
Some((Progress::Errored, State::Finished))
|
||||
Some(((id, Progress::Errored), State::Finished))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -68,7 +80,7 @@ where
|
|||
(downloaded as f32 / total as f32) * 100.0;
|
||||
|
||||
Some((
|
||||
Progress::Advanced(percentage),
|
||||
(id, Progress::Advanced(percentage)),
|
||||
State::Downloading {
|
||||
response,
|
||||
total,
|
||||
|
|
@ -76,8 +88,12 @@ where
|
|||
},
|
||||
))
|
||||
}
|
||||
Ok(None) => Some((Progress::Finished, State::Finished)),
|
||||
Err(_) => Some((Progress::Errored, State::Finished)),
|
||||
Ok(None) => {
|
||||
Some(((id, Progress::Finished), State::Finished))
|
||||
}
|
||||
Err(_) => {
|
||||
Some(((id, Progress::Errored), State::Finished))
|
||||
}
|
||||
},
|
||||
State::Finished => {
|
||||
// We do not let the stream die, as it would start a
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use iced::{
|
||||
button, executor, Align, Application, Button, Column, Command, Container,
|
||||
Element, Length, ProgressBar, Settings, Subscription, Text,
|
||||
button, executor, Align, Application, Button, Clipboard, Column, Command,
|
||||
Container, Element, Length, ProgressBar, Settings, Subscription, Text,
|
||||
};
|
||||
|
||||
mod download;
|
||||
|
|
@ -10,17 +10,17 @@ pub fn main() -> iced::Result {
|
|||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Example {
|
||||
Idle { button: button::State },
|
||||
Downloading { progress: f32 },
|
||||
Finished { button: button::State },
|
||||
Errored { button: button::State },
|
||||
struct Example {
|
||||
downloads: Vec<Download>,
|
||||
last_id: usize,
|
||||
add: button::State,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Message {
|
||||
Download,
|
||||
DownloadProgressed(download::Progress),
|
||||
Add,
|
||||
Download(usize),
|
||||
DownloadProgressed((usize, download::Progress)),
|
||||
}
|
||||
|
||||
impl Application for Example {
|
||||
|
|
@ -30,8 +30,10 @@ impl Application for Example {
|
|||
|
||||
fn new(_flags: ()) -> (Example, Command<Message>) {
|
||||
(
|
||||
Example::Idle {
|
||||
button: button::State::new(),
|
||||
Example {
|
||||
downloads: vec![Download::new(0)],
|
||||
last_id: 0,
|
||||
add: button::State::new(),
|
||||
},
|
||||
Command::none(),
|
||||
)
|
||||
|
|
@ -41,104 +43,177 @@ impl Application for Example {
|
|||
String::from("Download progress - Iced")
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
fn update(
|
||||
&mut self,
|
||||
message: Message,
|
||||
_clipboard: &mut Clipboard,
|
||||
) -> Command<Message> {
|
||||
match message {
|
||||
Message::Download => match self {
|
||||
Example::Idle { .. }
|
||||
| Example::Finished { .. }
|
||||
| Example::Errored { .. } => {
|
||||
*self = Example::Downloading { progress: 0.0 };
|
||||
Message::Add => {
|
||||
self.last_id = self.last_id + 1;
|
||||
|
||||
self.downloads.push(Download::new(self.last_id));
|
||||
}
|
||||
Message::Download(index) => {
|
||||
if let Some(download) = self.downloads.get_mut(index) {
|
||||
download.start();
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
Message::DownloadProgressed(message) => match self {
|
||||
Example::Downloading { progress } => match message {
|
||||
download::Progress::Started => {
|
||||
*progress = 0.0;
|
||||
}
|
||||
download::Progress::Advanced(percentage) => {
|
||||
*progress = percentage;
|
||||
}
|
||||
download::Progress::Finished => {
|
||||
*self = Example::Finished {
|
||||
button: button::State::new(),
|
||||
}
|
||||
}
|
||||
download::Progress::Errored => {
|
||||
*self = Example::Errored {
|
||||
button: button::State::new(),
|
||||
};
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
Message::DownloadProgressed((id, progress)) => {
|
||||
if let Some(download) =
|
||||
self.downloads.iter_mut().find(|download| download.id == id)
|
||||
{
|
||||
download.progress(progress);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Message> {
|
||||
match self {
|
||||
Example::Downloading { .. } => {
|
||||
download::file("https://speed.hetzner.de/100MB.bin")
|
||||
Subscription::batch(self.downloads.iter().map(Download::subscription))
|
||||
}
|
||||
|
||||
fn view(&mut self) -> Element<Message> {
|
||||
let downloads = self
|
||||
.downloads
|
||||
.iter_mut()
|
||||
.fold(Column::new().spacing(20), |column, download| {
|
||||
column.push(download.view())
|
||||
})
|
||||
.push(
|
||||
Button::new(&mut self.add, Text::new("Add another download"))
|
||||
.on_press(Message::Add)
|
||||
.padding(10),
|
||||
)
|
||||
.align_items(Align::End);
|
||||
|
||||
Container::new(downloads)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.center_x()
|
||||
.center_y()
|
||||
.padding(20)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Download {
|
||||
id: usize,
|
||||
state: State,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum State {
|
||||
Idle { button: button::State },
|
||||
Downloading { progress: f32 },
|
||||
Finished { button: button::State },
|
||||
Errored { button: button::State },
|
||||
}
|
||||
|
||||
impl Download {
|
||||
pub fn new(id: usize) -> Self {
|
||||
Download {
|
||||
id,
|
||||
state: State::Idle {
|
||||
button: button::State::new(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&mut self) {
|
||||
match self.state {
|
||||
State::Idle { .. }
|
||||
| State::Finished { .. }
|
||||
| State::Errored { .. } => {
|
||||
self.state = State::Downloading { progress: 0.0 };
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn progress(&mut self, new_progress: download::Progress) {
|
||||
match &mut self.state {
|
||||
State::Downloading { progress } => match new_progress {
|
||||
download::Progress::Started => {
|
||||
*progress = 0.0;
|
||||
}
|
||||
download::Progress::Advanced(percentage) => {
|
||||
*progress = percentage;
|
||||
}
|
||||
download::Progress::Finished => {
|
||||
self.state = State::Finished {
|
||||
button: button::State::new(),
|
||||
}
|
||||
}
|
||||
download::Progress::Errored => {
|
||||
self.state = State::Errored {
|
||||
button: button::State::new(),
|
||||
};
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn subscription(&self) -> Subscription<Message> {
|
||||
match self.state {
|
||||
State::Downloading { .. } => {
|
||||
download::file(self.id, "https://speed.hetzner.de/100MB.bin?")
|
||||
.map(Message::DownloadProgressed)
|
||||
}
|
||||
_ => Subscription::none(),
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&mut self) -> Element<Message> {
|
||||
let current_progress = match self {
|
||||
Example::Idle { .. } => 0.0,
|
||||
Example::Downloading { progress } => *progress,
|
||||
Example::Finished { .. } => 100.0,
|
||||
Example::Errored { .. } => 0.0,
|
||||
pub fn view(&mut self) -> Element<Message> {
|
||||
let current_progress = match &self.state {
|
||||
State::Idle { .. } => 0.0,
|
||||
State::Downloading { progress } => *progress,
|
||||
State::Finished { .. } => 100.0,
|
||||
State::Errored { .. } => 0.0,
|
||||
};
|
||||
|
||||
let progress_bar = ProgressBar::new(0.0..=100.0, current_progress);
|
||||
|
||||
let control: Element<_> = match self {
|
||||
Example::Idle { button } => {
|
||||
let control: Element<_> = match &mut self.state {
|
||||
State::Idle { button } => {
|
||||
Button::new(button, Text::new("Start the download!"))
|
||||
.on_press(Message::Download)
|
||||
.on_press(Message::Download(self.id))
|
||||
.into()
|
||||
}
|
||||
Example::Finished { button } => Column::new()
|
||||
State::Finished { button } => Column::new()
|
||||
.spacing(10)
|
||||
.align_items(Align::Center)
|
||||
.push(Text::new("Download finished!"))
|
||||
.push(
|
||||
Button::new(button, Text::new("Start again"))
|
||||
.on_press(Message::Download),
|
||||
.on_press(Message::Download(self.id)),
|
||||
)
|
||||
.into(),
|
||||
Example::Downloading { .. } => {
|
||||
State::Downloading { .. } => {
|
||||
Text::new(format!("Downloading... {:.2}%", current_progress))
|
||||
.into()
|
||||
}
|
||||
Example::Errored { button } => Column::new()
|
||||
State::Errored { button } => Column::new()
|
||||
.spacing(10)
|
||||
.align_items(Align::Center)
|
||||
.push(Text::new("Something went wrong :("))
|
||||
.push(
|
||||
Button::new(button, Text::new("Try again"))
|
||||
.on_press(Message::Download),
|
||||
.on_press(Message::Download(self.id)),
|
||||
)
|
||||
.into(),
|
||||
};
|
||||
|
||||
let content = Column::new()
|
||||
Column::new()
|
||||
.spacing(10)
|
||||
.padding(10)
|
||||
.align_items(Align::Center)
|
||||
.push(progress_bar)
|
||||
.push(control);
|
||||
|
||||
Container::new(content)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.center_x()
|
||||
.center_y()
|
||||
.push(control)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +1,30 @@
|
|||
use iced::{
|
||||
executor, Align, Application, Checkbox, Column, Command, Container,
|
||||
Element, Length, Settings, Subscription, Text,
|
||||
button, executor, Align, Application, Button, Checkbox, Clipboard, Column,
|
||||
Command, Container, Element, HorizontalAlignment, Length, Settings,
|
||||
Subscription, Text,
|
||||
};
|
||||
use iced_native::{window, Event};
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
Events::run(Settings::default())
|
||||
Events::run(Settings {
|
||||
exit_on_close_request: false,
|
||||
..Settings::default()
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Events {
|
||||
last: Vec<iced_native::Event>,
|
||||
enabled: bool,
|
||||
exit: button::State,
|
||||
should_exit: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Message {
|
||||
EventOccurred(iced_native::Event),
|
||||
Toggled(bool),
|
||||
Exit,
|
||||
}
|
||||
|
||||
impl Application for Events {
|
||||
|
|
@ -32,29 +40,41 @@ impl Application for Events {
|
|||
String::from("Events - Iced")
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
fn update(
|
||||
&mut self,
|
||||
message: Message,
|
||||
_clipboard: &mut Clipboard,
|
||||
) -> Command<Message> {
|
||||
match message {
|
||||
Message::EventOccurred(event) => {
|
||||
Message::EventOccurred(event) if self.enabled => {
|
||||
self.last.push(event);
|
||||
|
||||
if self.last.len() > 5 {
|
||||
let _ = self.last.remove(0);
|
||||
}
|
||||
}
|
||||
Message::EventOccurred(event) => {
|
||||
if let Event::Window(window::Event::CloseRequested) = event {
|
||||
self.should_exit = true;
|
||||
}
|
||||
}
|
||||
Message::Toggled(enabled) => {
|
||||
self.enabled = enabled;
|
||||
}
|
||||
Message::Exit => {
|
||||
self.should_exit = true;
|
||||
}
|
||||
};
|
||||
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Message> {
|
||||
if self.enabled {
|
||||
iced_native::subscription::events().map(Message::EventOccurred)
|
||||
} else {
|
||||
Subscription::none()
|
||||
}
|
||||
iced_native::subscription::events().map(Message::EventOccurred)
|
||||
}
|
||||
|
||||
fn should_exit(&self) -> bool {
|
||||
self.should_exit
|
||||
}
|
||||
|
||||
fn view(&mut self) -> Element<Message> {
|
||||
|
|
@ -71,11 +91,22 @@ impl Application for Events {
|
|||
Message::Toggled,
|
||||
);
|
||||
|
||||
let exit = Button::new(
|
||||
&mut self.exit,
|
||||
Text::new("Exit")
|
||||
.width(Length::Fill)
|
||||
.horizontal_alignment(HorizontalAlignment::Center),
|
||||
)
|
||||
.width(Length::Units(100))
|
||||
.padding(10)
|
||||
.on_press(Message::Exit);
|
||||
|
||||
let content = Column::new()
|
||||
.align_items(Align::Center)
|
||||
.spacing(20)
|
||||
.push(events)
|
||||
.push(toggle);
|
||||
.push(toggle)
|
||||
.push(exit);
|
||||
|
||||
Container::new(content)
|
||||
.width(Length::Fill)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,6 @@ publish = false
|
|||
|
||||
[dependencies]
|
||||
iced = { path = "../..", features = ["canvas", "tokio", "debug"] }
|
||||
tokio = { version = "0.3", features = ["sync"] }
|
||||
tokio = { version = "1.0", features = ["sync"] }
|
||||
itertools = "0.9"
|
||||
rustc-hash = "1.1"
|
||||
|
|
|
|||
|
|
@ -6,12 +6,14 @@ mod style;
|
|||
use grid::Grid;
|
||||
use iced::button::{self, Button};
|
||||
use iced::executor;
|
||||
use iced::menu::{self, Menu};
|
||||
use iced::pick_list::{self, PickList};
|
||||
use iced::slider::{self, Slider};
|
||||
use iced::time;
|
||||
use iced::window;
|
||||
use iced::{
|
||||
Align, Application, Checkbox, Column, Command, Container, Element, Length,
|
||||
Row, Settings, Subscription, Text,
|
||||
Align, Application, Checkbox, Clipboard, Column, Command, Container,
|
||||
Element, Length, Row, Settings, Subscription, Text,
|
||||
};
|
||||
use preset::Preset;
|
||||
use std::time::{Duration, Instant};
|
||||
|
|
@ -19,6 +21,10 @@ use std::time::{Duration, Instant};
|
|||
pub fn main() -> iced::Result {
|
||||
GameOfLife::run(Settings {
|
||||
antialiasing: true,
|
||||
window: window::Settings {
|
||||
position: window::Position::Centered,
|
||||
..window::Settings::default()
|
||||
},
|
||||
..Settings::default()
|
||||
})
|
||||
}
|
||||
|
|
@ -65,7 +71,11 @@ impl Application for GameOfLife {
|
|||
String::from("Game of Life - Iced")
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
fn update(
|
||||
&mut self,
|
||||
message: Message,
|
||||
_clipboard: &mut Clipboard,
|
||||
) -> Command<Message> {
|
||||
match message {
|
||||
Message::Grid(message, version) => {
|
||||
if version == self.version {
|
||||
|
|
@ -124,6 +134,13 @@ impl Application for GameOfLife {
|
|||
}
|
||||
}
|
||||
|
||||
fn menu(&self) -> Menu<Message> {
|
||||
Menu::with_entries(vec![menu::Entry::dropdown(
|
||||
"Presets",
|
||||
Preset::menu().map(Message::PresetPicked),
|
||||
)])
|
||||
}
|
||||
|
||||
fn view(&mut self) -> Element<Message> {
|
||||
let version = self.version;
|
||||
let selected_speed = self.next_speed.unwrap_or(self.speed);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
use iced::menu::{self, Menu};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum Preset {
|
||||
Custom,
|
||||
|
|
@ -26,6 +28,17 @@ pub static ALL: &[Preset] = &[
|
|||
];
|
||||
|
||||
impl Preset {
|
||||
pub fn menu() -> Menu<Self> {
|
||||
Menu::with_entries(
|
||||
ALL.iter()
|
||||
.copied()
|
||||
.map(|preset| {
|
||||
menu::Entry::item(preset.to_string(), None, preset)
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn life(self) -> Vec<(isize, isize)> {
|
||||
#[rustfmt::skip]
|
||||
let cells = match self {
|
||||
|
|
|
|||
|
|
@ -171,6 +171,7 @@ impl pick_list::StyleSheet for PickList {
|
|||
},
|
||||
border_radius: 2.0,
|
||||
icon_size: 0.5,
|
||||
..pick_list::Style::default()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use iced_wgpu::Renderer;
|
||||
use iced_winit::{
|
||||
slider, Align, Color, Column, Command, Element, Length, Program, Row,
|
||||
Slider, Text,
|
||||
slider, Align, Clipboard, Color, Column, Command, Element, Length, Program,
|
||||
Row, Slider, Text,
|
||||
};
|
||||
|
||||
pub struct Controls {
|
||||
|
|
@ -30,8 +30,13 @@ impl Controls {
|
|||
impl Program for Controls {
|
||||
type Renderer = Renderer;
|
||||
type Message = Message;
|
||||
type Clipboard = Clipboard;
|
||||
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
fn update(
|
||||
&mut self,
|
||||
message: Message,
|
||||
_clipboard: &mut Clipboard,
|
||||
) -> Command<Message> {
|
||||
match message {
|
||||
Message::BackgroundColorChanged(color) => {
|
||||
self.background_color = color;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use controls::Controls;
|
|||
use scene::Scene;
|
||||
|
||||
use iced_wgpu::{wgpu, Backend, Renderer, Settings, Viewport};
|
||||
use iced_winit::{conversion, futures, program, winit, Debug, Size};
|
||||
use iced_winit::{conversion, futures, program, winit, Clipboard, Debug, Size};
|
||||
|
||||
use futures::task::SpawnExt;
|
||||
use winit::{
|
||||
|
|
@ -28,6 +28,7 @@ pub fn main() {
|
|||
);
|
||||
let mut cursor_position = PhysicalPosition::new(-1.0, -1.0);
|
||||
let mut modifiers = ModifiersState::default();
|
||||
let mut clipboard = Clipboard::connect(&window);
|
||||
|
||||
// Initialize wgpu
|
||||
let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
|
||||
|
|
@ -36,7 +37,7 @@ pub fn main() {
|
|||
let (mut device, queue) = futures::executor::block_on(async {
|
||||
let adapter = instance
|
||||
.request_adapter(&wgpu::RequestAdapterOptions {
|
||||
power_preference: wgpu::PowerPreference::Default,
|
||||
power_preference: wgpu::PowerPreference::HighPerformance,
|
||||
compatible_surface: Some(&surface),
|
||||
})
|
||||
.await
|
||||
|
|
@ -45,9 +46,9 @@ pub fn main() {
|
|||
adapter
|
||||
.request_device(
|
||||
&wgpu::DeviceDescriptor {
|
||||
label: None,
|
||||
features: wgpu::Features::empty(),
|
||||
limits: wgpu::Limits::default(),
|
||||
shader_validation: false,
|
||||
},
|
||||
None,
|
||||
)
|
||||
|
|
@ -63,7 +64,7 @@ pub fn main() {
|
|||
device.create_swap_chain(
|
||||
&surface,
|
||||
&wgpu::SwapChainDescriptor {
|
||||
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
|
||||
usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
|
||||
format: format,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
|
|
@ -141,8 +142,8 @@ pub fn main() {
|
|||
cursor_position,
|
||||
viewport.scale_factor(),
|
||||
),
|
||||
None,
|
||||
&mut renderer,
|
||||
&mut clipboard,
|
||||
&mut debug,
|
||||
);
|
||||
|
||||
|
|
@ -157,7 +158,7 @@ pub fn main() {
|
|||
swap_chain = device.create_swap_chain(
|
||||
&surface,
|
||||
&wgpu::SwapChainDescriptor {
|
||||
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
|
||||
usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
|
||||
format: format,
|
||||
width: size.width,
|
||||
height: size.height,
|
||||
|
|
|
|||
|
|
@ -19,8 +19,9 @@ impl Scene {
|
|||
background_color: Color,
|
||||
) -> wgpu::RenderPass<'a> {
|
||||
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
|
||||
attachment: target,
|
||||
label: None,
|
||||
color_attachments: &[wgpu::RenderPassColorAttachment {
|
||||
view: target,
|
||||
resolve_target: None,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear({
|
||||
|
|
@ -48,10 +49,10 @@ impl Scene {
|
|||
|
||||
fn build_pipeline(device: &wgpu::Device) -> wgpu::RenderPipeline {
|
||||
let vs_module =
|
||||
device.create_shader_module(wgpu::include_spirv!("shader/vert.spv"));
|
||||
device.create_shader_module(&wgpu::include_spirv!("shader/vert.spv"));
|
||||
|
||||
let fs_module =
|
||||
device.create_shader_module(wgpu::include_spirv!("shader/frag.spv"));
|
||||
device.create_shader_module(&wgpu::include_spirv!("shader/frag.spv"));
|
||||
|
||||
let pipeline_layout =
|
||||
device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
|
||||
|
|
@ -64,34 +65,34 @@ fn build_pipeline(device: &wgpu::Device) -> wgpu::RenderPipeline {
|
|||
device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
|
||||
label: None,
|
||||
layout: Some(&pipeline_layout),
|
||||
vertex_stage: wgpu::ProgrammableStageDescriptor {
|
||||
vertex: wgpu::VertexState {
|
||||
module: &vs_module,
|
||||
entry_point: "main",
|
||||
buffers: &[],
|
||||
},
|
||||
fragment_stage: Some(wgpu::ProgrammableStageDescriptor {
|
||||
fragment: Some(wgpu::FragmentState {
|
||||
module: &fs_module,
|
||||
entry_point: "main",
|
||||
targets: &[wgpu::ColorTargetState {
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||
blend: Some(wgpu::BlendState {
|
||||
color: wgpu::BlendComponent::REPLACE,
|
||||
alpha: wgpu::BlendComponent::REPLACE,
|
||||
}),
|
||||
write_mask: wgpu::ColorWrite::ALL,
|
||||
}],
|
||||
}),
|
||||
rasterization_state: Some(wgpu::RasterizationStateDescriptor {
|
||||
primitive: wgpu::PrimitiveState {
|
||||
topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
front_face: wgpu::FrontFace::Ccw,
|
||||
cull_mode: wgpu::CullMode::None,
|
||||
..Default::default()
|
||||
}),
|
||||
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
|
||||
color_states: &[wgpu::ColorStateDescriptor {
|
||||
format: wgpu::TextureFormat::Bgra8UnormSrgb,
|
||||
color_blend: wgpu::BlendDescriptor::REPLACE,
|
||||
alpha_blend: wgpu::BlendDescriptor::REPLACE,
|
||||
write_mask: wgpu::ColorWrite::ALL,
|
||||
}],
|
||||
depth_stencil_state: None,
|
||||
vertex_state: wgpu::VertexStateDescriptor {
|
||||
index_format: wgpu::IndexFormat::Uint16,
|
||||
vertex_buffers: &[],
|
||||
},
|
||||
sample_count: 1,
|
||||
sample_mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
depth_stencil: None,
|
||||
multisample: wgpu::MultisampleState {
|
||||
count: 1,
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
});
|
||||
|
||||
pipeline
|
||||
|
|
|
|||
10
examples/menu/Cargo.toml
Normal file
10
examples/menu/Cargo.toml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "menu"
|
||||
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" }
|
||||
117
examples/menu/src/main.rs
Normal file
117
examples/menu/src/main.rs
Normal file
|
|
@ -0,0 +1,117 @@
|
|||
use iced::menu::{self, Menu};
|
||||
use iced::{
|
||||
executor, Application, Clipboard, Command, Container, Element, Length,
|
||||
Settings, Text,
|
||||
};
|
||||
use iced_native::keyboard::{Hotkey, KeyCode, Modifiers};
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
App::run(Settings::default())
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct App {
|
||||
selected: Option<Entry>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Entry {
|
||||
One,
|
||||
Two,
|
||||
Three,
|
||||
A,
|
||||
B,
|
||||
C,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Message {
|
||||
MenuActivated(Entry),
|
||||
}
|
||||
|
||||
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("Menu - Iced")
|
||||
}
|
||||
|
||||
fn menu(&self) -> Menu<Message> {
|
||||
let alt = Modifiers::ALT;
|
||||
let ctrl_shift = Modifiers::CTRL | Modifiers::SHIFT;
|
||||
|
||||
Menu::with_entries(vec![
|
||||
menu::Entry::dropdown(
|
||||
"First",
|
||||
Menu::with_entries(vec![
|
||||
menu::Entry::item(
|
||||
"One",
|
||||
Hotkey::new(alt, KeyCode::F1),
|
||||
Message::MenuActivated(Entry::One),
|
||||
),
|
||||
menu::Entry::item(
|
||||
"Two",
|
||||
Hotkey::new(alt, KeyCode::F2),
|
||||
Message::MenuActivated(Entry::Two),
|
||||
),
|
||||
menu::Entry::Separator,
|
||||
menu::Entry::item(
|
||||
"Three",
|
||||
Hotkey::new(alt, KeyCode::F3),
|
||||
Message::MenuActivated(Entry::Three),
|
||||
),
|
||||
]),
|
||||
),
|
||||
menu::Entry::dropdown(
|
||||
"Second",
|
||||
Menu::with_entries(vec![
|
||||
menu::Entry::item(
|
||||
"A",
|
||||
Hotkey::new(ctrl_shift, KeyCode::A),
|
||||
Message::MenuActivated(Entry::A),
|
||||
),
|
||||
menu::Entry::item(
|
||||
"B",
|
||||
Hotkey::new(ctrl_shift, KeyCode::B),
|
||||
Message::MenuActivated(Entry::B),
|
||||
),
|
||||
menu::Entry::Separator,
|
||||
menu::Entry::item(
|
||||
"C",
|
||||
Hotkey::new(ctrl_shift, KeyCode::C),
|
||||
Message::MenuActivated(Entry::C),
|
||||
),
|
||||
]),
|
||||
),
|
||||
])
|
||||
}
|
||||
|
||||
fn update(
|
||||
&mut self,
|
||||
message: Message,
|
||||
_clipboard: &mut Clipboard,
|
||||
) -> Command<Message> {
|
||||
match message {
|
||||
Message::MenuActivated(entry) => self.selected = Some(entry),
|
||||
}
|
||||
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn view(&mut self) -> Element<Message> {
|
||||
Container::new(
|
||||
Text::new(format!("Selected {:?}", self.selected)).size(48),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.center_x()
|
||||
.center_y()
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
use iced::{
|
||||
button, executor, keyboard, pane_grid, scrollable, Align, Application,
|
||||
Button, Column, Command, Container, Element, HorizontalAlignment, Length,
|
||||
PaneGrid, Scrollable, Settings, Subscription, Text,
|
||||
Button, Clipboard, Color, Column, Command, Container, Element,
|
||||
HorizontalAlignment, Length, PaneGrid, Row, Scrollable, Settings,
|
||||
Subscription, Text,
|
||||
};
|
||||
use iced_native::{event, subscription, Event};
|
||||
|
||||
|
|
@ -10,7 +11,7 @@ pub fn main() -> iced::Result {
|
|||
}
|
||||
|
||||
struct Example {
|
||||
panes: pane_grid::State<Content>,
|
||||
panes: pane_grid::State<Pane>,
|
||||
panes_created: usize,
|
||||
focus: Option<pane_grid::Pane>,
|
||||
}
|
||||
|
|
@ -23,6 +24,7 @@ enum Message {
|
|||
Clicked(pane_grid::Pane),
|
||||
Dragged(pane_grid::DragEvent),
|
||||
Resized(pane_grid::ResizeEvent),
|
||||
TogglePin(pane_grid::Pane),
|
||||
Close(pane_grid::Pane),
|
||||
CloseFocused,
|
||||
}
|
||||
|
|
@ -33,7 +35,7 @@ impl Application for Example {
|
|||
type Flags = ();
|
||||
|
||||
fn new(_flags: ()) -> (Self, Command<Message>) {
|
||||
let (panes, _) = pane_grid::State::new(Content::new(0));
|
||||
let (panes, _) = pane_grid::State::new(Pane::new(0));
|
||||
|
||||
(
|
||||
Example {
|
||||
|
|
@ -49,13 +51,17 @@ impl Application for Example {
|
|||
String::from("Pane grid - Iced")
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
fn update(
|
||||
&mut self,
|
||||
message: Message,
|
||||
_clipboard: &mut Clipboard,
|
||||
) -> Command<Message> {
|
||||
match message {
|
||||
Message::Split(axis, pane) => {
|
||||
let result = self.panes.split(
|
||||
axis,
|
||||
&pane,
|
||||
Content::new(self.panes_created),
|
||||
Pane::new(self.panes_created),
|
||||
);
|
||||
|
||||
if let Some((pane, _)) = result {
|
||||
|
|
@ -69,7 +75,7 @@ impl Application for Example {
|
|||
let result = self.panes.split(
|
||||
axis,
|
||||
&pane,
|
||||
Content::new(self.panes_created),
|
||||
Pane::new(self.panes_created),
|
||||
);
|
||||
|
||||
if let Some((pane, _)) = result {
|
||||
|
|
@ -101,6 +107,12 @@ impl Application for Example {
|
|||
self.panes.swap(&pane, &target);
|
||||
}
|
||||
Message::Dragged(_) => {}
|
||||
Message::TogglePin(pane) => {
|
||||
if let Some(Pane { is_pinned, .. }) = self.panes.get_mut(&pane)
|
||||
{
|
||||
*is_pinned = !*is_pinned;
|
||||
}
|
||||
}
|
||||
Message::Close(pane) => {
|
||||
if let Some((_, sibling)) = self.panes.close(&pane) {
|
||||
self.focus = Some(sibling);
|
||||
|
|
@ -108,8 +120,14 @@ impl Application for Example {
|
|||
}
|
||||
Message::CloseFocused => {
|
||||
if let Some(pane) = self.focus {
|
||||
if let Some((_, sibling)) = self.panes.close(&pane) {
|
||||
self.focus = Some(sibling);
|
||||
if let Some(Pane { is_pinned, .. }) = self.panes.get(&pane)
|
||||
{
|
||||
if !is_pinned {
|
||||
if let Some((_, sibling)) = self.panes.close(&pane)
|
||||
{
|
||||
self.focus = Some(sibling);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -128,7 +146,7 @@ impl Application for Example {
|
|||
Event::Keyboard(keyboard::Event::KeyPressed {
|
||||
modifiers,
|
||||
key_code,
|
||||
}) if modifiers.is_command_pressed() => handle_hotkey(key_code),
|
||||
}) if modifiers.command() => handle_hotkey(key_code),
|
||||
_ => None,
|
||||
}
|
||||
})
|
||||
|
|
@ -138,17 +156,41 @@ impl Application for Example {
|
|||
let focus = self.focus;
|
||||
let total_panes = self.panes.len();
|
||||
|
||||
let pane_grid = PaneGrid::new(&mut self.panes, |pane, content| {
|
||||
let is_focused = focus == Some(pane);
|
||||
let pane_grid = PaneGrid::new(&mut self.panes, |id, pane| {
|
||||
let is_focused = focus == Some(id);
|
||||
|
||||
let title_bar =
|
||||
pane_grid::TitleBar::new(format!("Pane {}", content.id))
|
||||
.padding(10)
|
||||
.style(style::TitleBar { is_focused });
|
||||
let text = if pane.is_pinned { "Unpin" } else { "Pin" };
|
||||
let pin_button =
|
||||
Button::new(&mut pane.pin_button, Text::new(text).size(14))
|
||||
.on_press(Message::TogglePin(id))
|
||||
.style(style::Button::Pin)
|
||||
.padding(3);
|
||||
|
||||
pane_grid::Content::new(content.view(pane, total_panes))
|
||||
.title_bar(title_bar)
|
||||
.style(style::Pane { is_focused })
|
||||
let title = Row::with_children(vec![
|
||||
pin_button.into(),
|
||||
Text::new("Pane").into(),
|
||||
Text::new(pane.content.id.to_string())
|
||||
.color(if is_focused {
|
||||
PANE_ID_COLOR_FOCUSED
|
||||
} else {
|
||||
PANE_ID_COLOR_UNFOCUSED
|
||||
})
|
||||
.into(),
|
||||
])
|
||||
.spacing(5);
|
||||
|
||||
let title_bar = pane_grid::TitleBar::new(title)
|
||||
.controls(pane.controls.view(id, total_panes, pane.is_pinned))
|
||||
.padding(10)
|
||||
.style(style::TitleBar { is_focused });
|
||||
|
||||
pane_grid::Content::new(pane.content.view(
|
||||
id,
|
||||
total_panes,
|
||||
pane.is_pinned,
|
||||
))
|
||||
.title_bar(title_bar)
|
||||
.style(style::Pane { is_focused })
|
||||
})
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
|
|
@ -165,6 +207,17 @@ impl Application for Example {
|
|||
}
|
||||
}
|
||||
|
||||
const PANE_ID_COLOR_UNFOCUSED: Color = Color::from_rgb(
|
||||
0xFF as f32 / 255.0,
|
||||
0xC7 as f32 / 255.0,
|
||||
0xC7 as f32 / 255.0,
|
||||
);
|
||||
const PANE_ID_COLOR_FOCUSED: Color = Color::from_rgb(
|
||||
0xFF as f32 / 255.0,
|
||||
0x47 as f32 / 255.0,
|
||||
0x47 as f32 / 255.0,
|
||||
);
|
||||
|
||||
fn handle_hotkey(key_code: keyboard::KeyCode) -> Option<Message> {
|
||||
use keyboard::KeyCode;
|
||||
use pane_grid::{Axis, Direction};
|
||||
|
|
@ -185,6 +238,13 @@ fn handle_hotkey(key_code: keyboard::KeyCode) -> Option<Message> {
|
|||
}
|
||||
}
|
||||
|
||||
struct Pane {
|
||||
pub is_pinned: bool,
|
||||
pub pin_button: button::State,
|
||||
pub content: Content,
|
||||
pub controls: Controls,
|
||||
}
|
||||
|
||||
struct Content {
|
||||
id: usize,
|
||||
scroll: scrollable::State,
|
||||
|
|
@ -193,6 +253,21 @@ struct Content {
|
|||
close: button::State,
|
||||
}
|
||||
|
||||
struct Controls {
|
||||
close: button::State,
|
||||
}
|
||||
|
||||
impl Pane {
|
||||
fn new(id: usize) -> Self {
|
||||
Self {
|
||||
is_pinned: false,
|
||||
pin_button: button::State::new(),
|
||||
content: Content::new(id),
|
||||
controls: Controls::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Content {
|
||||
fn new(id: usize) -> Self {
|
||||
Content {
|
||||
|
|
@ -207,6 +282,7 @@ impl Content {
|
|||
&mut self,
|
||||
pane: pane_grid::Pane,
|
||||
total_panes: usize,
|
||||
is_pinned: bool,
|
||||
) -> Element<Message> {
|
||||
let Content {
|
||||
scroll,
|
||||
|
|
@ -246,7 +322,7 @@ impl Content {
|
|||
style::Button::Primary,
|
||||
));
|
||||
|
||||
if total_panes > 1 {
|
||||
if total_panes > 1 && !is_pinned {
|
||||
controls = controls.push(button(
|
||||
close,
|
||||
"Close",
|
||||
|
|
@ -270,7 +346,32 @@ impl Content {
|
|||
}
|
||||
}
|
||||
|
||||
impl Controls {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
close: button::State::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view(
|
||||
&mut self,
|
||||
pane: pane_grid::Pane,
|
||||
total_panes: usize,
|
||||
is_pinned: bool,
|
||||
) -> Element<Message> {
|
||||
let mut button =
|
||||
Button::new(&mut self.close, Text::new("Close").size(14))
|
||||
.style(style::Button::Control)
|
||||
.padding(3);
|
||||
if total_panes > 1 && !is_pinned {
|
||||
button = button.on_press(Message::Close(pane));
|
||||
}
|
||||
button.into()
|
||||
}
|
||||
}
|
||||
|
||||
mod style {
|
||||
use crate::PANE_ID_COLOR_FOCUSED;
|
||||
use iced::{button, container, Background, Color, Vector};
|
||||
|
||||
const SURFACE: Color = Color::from_rgb(
|
||||
|
|
@ -332,6 +433,8 @@ mod style {
|
|||
pub enum Button {
|
||||
Primary,
|
||||
Destructive,
|
||||
Control,
|
||||
Pin,
|
||||
}
|
||||
|
||||
impl button::StyleSheet for Button {
|
||||
|
|
@ -341,6 +444,8 @@ mod style {
|
|||
Button::Destructive => {
|
||||
(None, Color::from_rgb8(0xFF, 0x47, 0x47))
|
||||
}
|
||||
Button::Control => (Some(PANE_ID_COLOR_FOCUSED), Color::WHITE),
|
||||
Button::Pin => (Some(ACTIVE), Color::WHITE),
|
||||
};
|
||||
|
||||
button::Style {
|
||||
|
|
@ -361,6 +466,8 @@ mod style {
|
|||
a: 0.2,
|
||||
..active.text_color
|
||||
}),
|
||||
Button::Control => Some(PANE_ID_COLOR_FOCUSED),
|
||||
Button::Pin => Some(HOVERED),
|
||||
};
|
||||
|
||||
button::Style {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ pub fn main() -> iced::Result {
|
|||
struct Example {
|
||||
scroll: scrollable::State,
|
||||
pick_list: pick_list::State<Language>,
|
||||
selected_language: Language,
|
||||
selected_language: Option<Language>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
|
|
@ -33,7 +33,7 @@ impl Sandbox for Example {
|
|||
fn update(&mut self, message: Message) {
|
||||
match message {
|
||||
Message::LanguageSelected(language) => {
|
||||
self.selected_language = language;
|
||||
self.selected_language = Some(language);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -42,9 +42,10 @@ impl Sandbox for Example {
|
|||
let pick_list = PickList::new(
|
||||
&mut self.pick_list,
|
||||
&Language::ALL[..],
|
||||
Some(self.selected_language),
|
||||
self.selected_language,
|
||||
Message::LanguageSelected,
|
||||
);
|
||||
)
|
||||
.placeholder("Choose a language...");
|
||||
|
||||
let mut content = Scrollable::new(&mut self.scroll)
|
||||
.width(Length::Fill)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use iced::{
|
||||
button, futures, image, Align, Application, Button, Column, Command,
|
||||
Container, Element, Image, Length, Row, Settings, Text,
|
||||
button, futures, image, Align, Application, Button, Clipboard, Column,
|
||||
Command, Container, Element, Length, Row, Settings, Text,
|
||||
};
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
|
|
@ -48,7 +48,11 @@ impl Application for Pokedex {
|
|||
format!("{} - Pokédex", subtitle)
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
fn update(
|
||||
&mut self,
|
||||
message: Message,
|
||||
_clipboard: &mut Clipboard,
|
||||
) -> Command<Message> {
|
||||
match message {
|
||||
Message::PokemonFound(Ok(pokemon)) => {
|
||||
*self = Pokedex::Loaded {
|
||||
|
|
@ -112,16 +116,20 @@ struct Pokemon {
|
|||
name: String,
|
||||
description: String,
|
||||
image: image::Handle,
|
||||
image_viewer: image::viewer::State,
|
||||
}
|
||||
|
||||
impl Pokemon {
|
||||
const TOTAL: u16 = 807;
|
||||
|
||||
fn view(&self) -> Element<Message> {
|
||||
fn view(&mut self) -> Element<Message> {
|
||||
Row::new()
|
||||
.spacing(20)
|
||||
.align_items(Align::Center)
|
||||
.push(Image::new(self.image.clone()))
|
||||
.push(image::Viewer::new(
|
||||
&mut self.image_viewer,
|
||||
self.image.clone(),
|
||||
))
|
||||
.push(
|
||||
Column::new()
|
||||
.spacing(20)
|
||||
|
|
@ -200,11 +208,15 @@ impl Pokemon {
|
|||
.map(|c| if c.is_control() { ' ' } else { c })
|
||||
.collect(),
|
||||
image,
|
||||
image_viewer: image::viewer::State::new(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn fetch_image(id: u16) -> Result<image::Handle, reqwest::Error> {
|
||||
let url = format!("https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/{}.png", id);
|
||||
let url = format!(
|
||||
"https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/{}.png",
|
||||
id
|
||||
);
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6,4 +6,4 @@ edition = "2018"
|
|||
publish = false
|
||||
|
||||
[dependencies]
|
||||
iced = { path = "../.." }
|
||||
iced = { path = "../..", features = ["debug"] }
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
mod style;
|
||||
|
||||
use iced::{
|
||||
scrollable, 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,13 +84,28 @@ impl Sandbox for ScrollableDemo {
|
|||
let scrollable_row = Row::with_children(
|
||||
variants
|
||||
.iter_mut()
|
||||
.map(|variant| {
|
||||
let mut scrollable = Scrollable::new(&mut variant.state)
|
||||
.padding(10)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.style(*theme)
|
||||
.push(Text::new(variant.title));
|
||||
.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(
|
||||
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
|
||||
|
|
@ -108,12 +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(Text::new("The End."))
|
||||
.push(
|
||||
Button::new(
|
||||
&mut variant.scroll_to_top,
|
||||
Text::new("Scroll to top"),
|
||||
)
|
||||
.width(Length::Fill)
|
||||
.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(),
|
||||
|
|
@ -142,10 +198,13 @@ impl Sandbox for ScrollableDemo {
|
|||
/// A version of a scrollable
|
||||
struct Variant {
|
||||
title: &'static str,
|
||||
state: scrollable::State,
|
||||
scrollable: scrollable::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 {
|
||||
|
|
@ -153,31 +212,43 @@ impl Variant {
|
|||
vec![
|
||||
Self {
|
||||
title: "Default Scrollbar",
|
||||
state: scrollable::State::new(),
|
||||
scrollable: scrollable::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",
|
||||
state: scrollable::State::new(),
|
||||
scrollable: scrollable::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",
|
||||
state: scrollable::State::new(),
|
||||
scrollable: scrollable::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",
|
||||
state: scrollable::State::new(),
|
||||
scrollable: scrollable::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,
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,4 +7,4 @@ publish = false
|
|||
|
||||
[dependencies]
|
||||
iced = { path = "../..", features = ["canvas", "tokio", "debug"] }
|
||||
rand = "0.7"
|
||||
rand = "0.8.3"
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@
|
|||
//! [1]: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Basic_animations#An_animated_solar_system
|
||||
use iced::{
|
||||
canvas::{self, Cursor, Path, Stroke},
|
||||
executor, time, window, Application, Canvas, Color, Command, Element,
|
||||
Length, Point, Rectangle, Settings, Size, Subscription, Vector,
|
||||
executor, time, window, Application, Canvas, Clipboard, Color, Command,
|
||||
Element, Length, Point, Rectangle, Settings, Size, Subscription, Vector,
|
||||
};
|
||||
|
||||
use std::time::Instant;
|
||||
|
|
@ -48,7 +48,11 @@ impl Application for SolarSystem {
|
|||
String::from("Solar system - Iced")
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
fn update(
|
||||
&mut self,
|
||||
message: Message,
|
||||
_clipboard: &mut Clipboard,
|
||||
) -> Command<Message> {
|
||||
match message {
|
||||
Message::Tick(instant) => {
|
||||
self.state.update(instant);
|
||||
|
|
@ -117,15 +121,13 @@ impl State {
|
|||
(
|
||||
Point::new(
|
||||
rng.gen_range(
|
||||
-(width as f32) / 2.0,
|
||||
width as f32 / 2.0,
|
||||
(-(width as f32) / 2.0)..(width as f32 / 2.0),
|
||||
),
|
||||
rng.gen_range(
|
||||
-(height as f32) / 2.0,
|
||||
height as f32 / 2.0,
|
||||
(-(height as f32) / 2.0)..(height as f32 / 2.0),
|
||||
),
|
||||
),
|
||||
rng.gen_range(0.5, 1.0),
|
||||
rng.gen_range(0.5..1.0),
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
|
|
|
|||
|
|
@ -6,4 +6,4 @@ edition = "2018"
|
|||
publish = false
|
||||
|
||||
[dependencies]
|
||||
iced = { path = "../..", features = ["tokio"] }
|
||||
iced = { path = "../..", features = ["smol"] }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use iced::{
|
||||
button, executor, time, Align, Application, Button, Column, Command,
|
||||
Container, Element, HorizontalAlignment, Length, Row, Settings,
|
||||
button, executor, time, Align, Application, Button, Clipboard, Column,
|
||||
Command, Container, Element, HorizontalAlignment, Length, Row, Settings,
|
||||
Subscription, Text,
|
||||
};
|
||||
use std::time::{Duration, Instant};
|
||||
|
|
@ -49,7 +49,11 @@ impl Application for Stopwatch {
|
|||
String::from("Stopwatch - Iced")
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
fn update(
|
||||
&mut self,
|
||||
message: Message,
|
||||
_clipboard: &mut Clipboard,
|
||||
) -> Command<Message> {
|
||||
match message {
|
||||
Message::Toggle => match self.state {
|
||||
State::Idle => {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
@ -44,9 +46,10 @@ impl Sandbox for Styling {
|
|||
match message {
|
||||
Message::ThemeChanged(theme) => self.theme = theme,
|
||||
Message::InputChanged(value) => self.input_value = value,
|
||||
Message::ButtonPressed => (),
|
||||
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, text_input, Align, Application, Button, Checkbox,
|
||||
Column, Command, Container, Element, Font, HorizontalAlignment, Length,
|
||||
Row, Scrollable, Settings, Text, TextInput,
|
||||
Clipboard, Column, Command, Container, Element, Font, HorizontalAlignment,
|
||||
Length, Row, Scrollable, Settings, Text, TextInput,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
|
@ -58,7 +58,11 @@ impl Application for Todos {
|
|||
format!("Todos{} - Iced", if dirty { "*" } else { "" })
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) -> Command<Message> {
|
||||
fn update(
|
||||
&mut self,
|
||||
message: Message,
|
||||
_clipboard: &mut Clipboard,
|
||||
) -> Command<Message> {
|
||||
match self {
|
||||
Todos::Loading => {
|
||||
match message {
|
||||
|
|
@ -261,8 +265,11 @@ impl Task {
|
|||
self.completed = completed;
|
||||
}
|
||||
TaskMessage::Edit => {
|
||||
let mut text_input = text_input::State::focused();
|
||||
text_input.select_all();
|
||||
|
||||
self.state = TaskState::Editing {
|
||||
text_input: text_input::State::focused(),
|
||||
text_input,
|
||||
delete_button: button::State::new(),
|
||||
};
|
||||
}
|
||||
|
|
@ -489,7 +496,6 @@ enum LoadError {
|
|||
|
||||
#[derive(Debug, Clone)]
|
||||
enum SaveError {
|
||||
DirectoryError,
|
||||
FileError,
|
||||
WriteError,
|
||||
FormatError,
|
||||
|
|
@ -538,7 +544,7 @@ impl SavedState {
|
|||
if let Some(dir) = path.parent() {
|
||||
async_std::fs::create_dir_all(dir)
|
||||
.await
|
||||
.map_err(|_| SaveError::DirectoryError)?;
|
||||
.map_err(|_| SaveError::FileError)?;
|
||||
}
|
||||
|
||||
{
|
||||
|
|
|
|||
9
examples/tooltip/Cargo.toml
Normal file
9
examples/tooltip/Cargo.toml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
[package]
|
||||
name = "tooltip"
|
||||
version = "0.1.0"
|
||||
authors = ["Yusuf Bera Ertan <y.bera003.06@protonmail.com>"]
|
||||
edition = "2018"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
iced = { path = "../..", features = ["debug"] }
|
||||
14
examples/tooltip/README.md
Normal file
14
examples/tooltip/README.md
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
## Tooltip
|
||||
|
||||
A tooltip.
|
||||
|
||||
It displays and positions a widget on another based on cursor position.
|
||||
|
||||
The __[`main`]__ file contains all the code of the example.
|
||||
|
||||
You can run it with `cargo run`:
|
||||
```
|
||||
cargo run --package tooltip
|
||||
```
|
||||
|
||||
[`main`]: src/main.rs
|
||||
138
examples/tooltip/src/main.rs
Normal file
138
examples/tooltip/src/main.rs
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
use iced::tooltip::{self, Tooltip};
|
||||
use iced::{
|
||||
button, Button, Column, Container, Element, HorizontalAlignment, Length,
|
||||
Row, Sandbox, Settings, Text, VerticalAlignment,
|
||||
};
|
||||
|
||||
pub fn main() {
|
||||
Example::run(Settings::default()).unwrap()
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Example {
|
||||
top: button::State,
|
||||
bottom: button::State,
|
||||
right: button::State,
|
||||
left: button::State,
|
||||
follow_cursor: button::State,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Message;
|
||||
|
||||
impl Sandbox for Example {
|
||||
type Message = Message;
|
||||
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
String::from("Tooltip - Iced")
|
||||
}
|
||||
|
||||
fn update(&mut self, _message: Message) {}
|
||||
|
||||
fn view(&mut self) -> Element<Message> {
|
||||
let top =
|
||||
tooltip("Tooltip at top", &mut self.top, tooltip::Position::Top);
|
||||
|
||||
let bottom = tooltip(
|
||||
"Tooltip at bottom",
|
||||
&mut self.bottom,
|
||||
tooltip::Position::Bottom,
|
||||
);
|
||||
|
||||
let left =
|
||||
tooltip("Tooltip at left", &mut self.left, tooltip::Position::Left);
|
||||
|
||||
let right = tooltip(
|
||||
"Tooltip at right",
|
||||
&mut self.right,
|
||||
tooltip::Position::Right,
|
||||
);
|
||||
|
||||
let fixed_tooltips = Row::with_children(vec![
|
||||
top.into(),
|
||||
bottom.into(),
|
||||
left.into(),
|
||||
right.into(),
|
||||
])
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.align_items(iced::Align::Center)
|
||||
.spacing(50);
|
||||
|
||||
let follow_cursor = tooltip(
|
||||
"Tooltip follows cursor",
|
||||
&mut self.follow_cursor,
|
||||
tooltip::Position::FollowCursor,
|
||||
);
|
||||
|
||||
let content = Column::with_children(vec![
|
||||
Container::new(fixed_tooltips)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.center_x()
|
||||
.center_y()
|
||||
.into(),
|
||||
follow_cursor.into(),
|
||||
])
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.spacing(50);
|
||||
|
||||
Container::new(content)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.center_x()
|
||||
.center_y()
|
||||
.padding(50)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
fn tooltip<'a>(
|
||||
label: &str,
|
||||
button_state: &'a mut button::State,
|
||||
position: tooltip::Position,
|
||||
) -> Element<'a, Message> {
|
||||
Tooltip::new(
|
||||
Button::new(
|
||||
button_state,
|
||||
Text::new(label)
|
||||
.size(40)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill)
|
||||
.horizontal_alignment(HorizontalAlignment::Center)
|
||||
.vertical_alignment(VerticalAlignment::Center),
|
||||
)
|
||||
.on_press(Message)
|
||||
.width(Length::Fill)
|
||||
.height(Length::Fill),
|
||||
"Tooltip",
|
||||
position,
|
||||
)
|
||||
.gap(5)
|
||||
.padding(10)
|
||||
.style(style::Tooltip)
|
||||
.into()
|
||||
}
|
||||
|
||||
mod style {
|
||||
use iced::container;
|
||||
use iced::Color;
|
||||
|
||||
pub struct Tooltip;
|
||||
|
||||
impl container::StyleSheet for Tooltip {
|
||||
fn style(&self) -> container::Style {
|
||||
container::Style {
|
||||
text_color: Some(Color::from_rgb8(0xEE, 0xEE, 0xEE)),
|
||||
background: Some(Color::from_rgb(0.11, 0.42, 0.87).into()),
|
||||
border_radius: 12.0,
|
||||
..container::Style::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
10
examples/url_handler/Cargo.toml
Normal file
10
examples/url_handler/Cargo.toml
Normal file
|
|
@ -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" }
|
||||
73
examples/url_handler/src/main.rs
Normal file
73
examples/url_handler/src/main.rs
Normal file
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue