Merge branch 'master' of https://github.com/hecrj/iced into wgpu_outdatedframe

This commit is contained in:
Billy Messenger 2021-07-22 12:37:39 -05:00
commit e822f654e4
219 changed files with 6266 additions and 1761 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -171,6 +171,7 @@ impl pick_list::StyleSheet for PickList {
},
border_radius: 2.0,
icon_size: 0.5,
..pick_list::Style::default()
}
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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"))]
{

View file

@ -6,4 +6,4 @@ edition = "2018"
publish = false
[dependencies]
iced = { path = "../.." }
iced = { path = "../..", features = ["debug"] }

View file

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

View file

@ -7,4 +7,4 @@ publish = false
[dependencies]
iced = { path = "../..", features = ["canvas", "tokio", "debug"] }
rand = "0.7"
rand = "0.8.3"

View file

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

View file

@ -6,4 +6,4 @@ edition = "2018"
publish = false
[dependencies]
iced = { path = "../..", features = ["tokio"] }
iced = { path = "../..", features = ["smol"] }

View file

@ -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 => {

View file

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

View file

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

View 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"] }

View 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

View 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()
}
}
}
}

View file

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

View 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" }

View 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()
}
}