summaryrefslogtreecommitdiffstats
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/README.md4
-rw-r--r--examples/events/src/main.rs47
-rw-r--r--examples/game_of_life/src/main.rs13
-rw-r--r--examples/game_of_life/src/preset.rs13
-rw-r--r--examples/game_of_life/src/style.rs1
-rw-r--r--examples/integration_opengl/Cargo.toml13
-rw-r--r--examples/integration_opengl/README.md16
-rw-r--r--examples/integration_opengl/src/controls.rs110
-rw-r--r--examples/integration_opengl/src/main.rs181
-rw-r--r--examples/integration_opengl/src/scene.rs99
-rw-r--r--examples/integration_wgpu/Cargo.toml (renamed from examples/integration/Cargo.toml)4
-rw-r--r--examples/integration_wgpu/README.md (renamed from examples/integration/README.md)5
-rw-r--r--examples/integration_wgpu/src/controls.rs (renamed from examples/integration/src/controls.rs)0
-rw-r--r--examples/integration_wgpu/src/main.rs (renamed from examples/integration/src/main.rs)156
-rw-r--r--examples/integration_wgpu/src/scene.rs (renamed from examples/integration/src/scene.rs)13
-rw-r--r--examples/integration_wgpu/src/shader/frag.spv (renamed from examples/integration/src/shader/frag.spv)bin352 -> 352 bytes
-rw-r--r--examples/integration_wgpu/src/shader/vert.spv (renamed from examples/integration/src/shader/vert.spv)bin904 -> 904 bytes
-rw-r--r--examples/menu/Cargo.toml10
-rw-r--r--examples/menu/src/main.rs117
-rw-r--r--examples/pane_grid/src/main.rs108
-rw-r--r--examples/pick_list/src/main.rs9
-rw-r--r--examples/pokedex/src/main.rs5
-rw-r--r--examples/scrollable/Cargo.toml2
-rw-r--r--examples/scrollable/src/main.rs107
-rw-r--r--examples/styling/src/main.rs75
-rw-r--r--examples/todos/src/main.rs5
-rw-r--r--examples/tour/src/main.rs32
-rw-r--r--examples/url_handler/Cargo.toml10
-rw-r--r--examples/url_handler/src/main.rs73
29 files changed, 1087 insertions, 141 deletions
diff --git a/examples/README.md b/examples/README.md
index 10c28cf5..1b9f4b56 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -98,7 +98,8 @@ A bunch of simpler examples exist:
- [`download_progress`](download_progress), a basic application that asynchronously downloads a dummy file of 100 MB and tracks the download progress.
- [`events`](events), a log of native events displayed using a conditional `Subscription`.
- [`geometry`](geometry), a custom widget showcasing how to draw geometry with the `Mesh2D` primitive in [`iced_wgpu`](../wgpu).
-- [`integration`](integration), a demonstration of how to integrate Iced in an existing graphical application.
+- [`integration_opengl`](integration_opengl), a demonstration of how to integrate Iced in an existing OpenGL application.
+- [`integration_wgpu`](integration_wgpu), a demonstration of how to integrate Iced in an existing [`wgpu`] application.
- [`pane_grid`](pane_grid), a grid of panes that can be split, resized, and reorganized.
- [`pick_list`](pick_list), a dropdown list of selectable options.
- [`pokedex`](pokedex), an application that displays a random Pokédex entry (sprite included!) by using the [PokéAPI].
@@ -116,6 +117,7 @@ cargo run --package <example>
[`lyon`]: https://github.com/nical/lyon
[PokéAPI]: https://pokeapi.co/
[Ghostscript Tiger]: https://commons.wikimedia.org/wiki/File:Ghostscript_Tiger.svg
+[`wgpu`]: https://github.com/gfx-rs/wgpu
## [Coffee]
Since [Iced was born in May 2019], it has been powering the user interfaces in
diff --git a/examples/events/src/main.rs b/examples/events/src/main.rs
index 18e6364b..446c190b 100644
--- a/examples/events/src/main.rs
+++ b/examples/events/src/main.rs
@@ -1,22 +1,30 @@
use iced::{
- executor, Align, Application, Checkbox, Clipboard, 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 {
@@ -38,27 +46,35 @@ impl Application for Events {
_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> {
@@ -75,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)
diff --git a/examples/game_of_life/src/main.rs b/examples/game_of_life/src/main.rs
index 64599163..c3e16e8b 100644
--- a/examples/game_of_life/src/main.rs
+++ b/examples/game_of_life/src/main.rs
@@ -6,9 +6,11 @@ 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, Clipboard, Column, Command, Container,
Element, Length, Row, Settings, Subscription, Text,
@@ -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()
})
}
@@ -128,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);
diff --git a/examples/game_of_life/src/preset.rs b/examples/game_of_life/src/preset.rs
index 05157b6a..1c199a72 100644
--- a/examples/game_of_life/src/preset.rs
+++ b/examples/game_of_life/src/preset.rs
@@ -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 {
diff --git a/examples/game_of_life/src/style.rs b/examples/game_of_life/src/style.rs
index 6605826f..be9a0e96 100644
--- a/examples/game_of_life/src/style.rs
+++ b/examples/game_of_life/src/style.rs
@@ -171,6 +171,7 @@ impl pick_list::StyleSheet for PickList {
},
border_radius: 2.0,
icon_size: 0.5,
+ ..pick_list::Style::default()
}
}
diff --git a/examples/integration_opengl/Cargo.toml b/examples/integration_opengl/Cargo.toml
new file mode 100644
index 00000000..0917f2ec
--- /dev/null
+++ b/examples/integration_opengl/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "integration_opengl"
+version = "0.1.0"
+authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
+edition = "2018"
+publish = false
+
+[dependencies]
+iced_glutin = { path = "../../glutin" }
+iced_glow = { path = "../../glow" }
+iced_winit = { path = "../../winit" }
+env_logger = "0.8"
+glow = "0.6"
diff --git a/examples/integration_opengl/README.md b/examples/integration_opengl/README.md
new file mode 100644
index 00000000..08cce7a6
--- /dev/null
+++ b/examples/integration_opengl/README.md
@@ -0,0 +1,16 @@
+## OpenGL integration
+
+A demonstration of how to integrate Iced in an existing graphical OpenGL application.
+
+The __[`main`]__ file contains all the code of the example.
+
+<div align="center">
+ <a href="https://imgbox.com/9P9ETcod" target="_blank"><img src="https://images2.imgbox.com/2a/51/9P9ETcod_o.gif" alt="image host"/></a>
+</div>
+
+You can run it with `cargo run`:
+```
+cargo run --package integration_gl
+```
+
+[`main`]: src/main.rs
diff --git a/examples/integration_opengl/src/controls.rs b/examples/integration_opengl/src/controls.rs
new file mode 100644
index 00000000..13b7fbc2
--- /dev/null
+++ b/examples/integration_opengl/src/controls.rs
@@ -0,0 +1,110 @@
+use iced_glow::Renderer;
+use iced_glutin::{
+ slider, Align, Clipboard, Color, Column, Command, Element, Length, Program,
+ Row, Slider, Text,
+};
+
+pub struct Controls {
+ background_color: Color,
+ sliders: [slider::State; 3],
+}
+
+#[derive(Debug, Clone)]
+pub enum Message {
+ BackgroundColorChanged(Color),
+}
+
+impl Controls {
+ pub fn new() -> Controls {
+ Controls {
+ background_color: Color::BLACK,
+ sliders: Default::default(),
+ }
+ }
+
+ pub fn background_color(&self) -> Color {
+ self.background_color
+ }
+}
+
+impl Program for Controls {
+ type Renderer = Renderer;
+ type Message = Message;
+ type Clipboard = Clipboard;
+
+ fn update(
+ &mut self,
+ message: Message,
+ _clipboard: &mut Clipboard,
+ ) -> Command<Message> {
+ match message {
+ Message::BackgroundColorChanged(color) => {
+ self.background_color = color;
+ }
+ }
+
+ Command::none()
+ }
+
+ fn view(&mut self) -> Element<Message, Renderer> {
+ let [r, g, b] = &mut self.sliders;
+ let background_color = self.background_color;
+
+ let sliders = Row::new()
+ .width(Length::Units(500))
+ .spacing(20)
+ .push(
+ Slider::new(r, 0.0..=1.0, background_color.r, move |r| {
+ Message::BackgroundColorChanged(Color {
+ r,
+ ..background_color
+ })
+ })
+ .step(0.01),
+ )
+ .push(
+ Slider::new(g, 0.0..=1.0, background_color.g, move |g| {
+ Message::BackgroundColorChanged(Color {
+ g,
+ ..background_color
+ })
+ })
+ .step(0.01),
+ )
+ .push(
+ Slider::new(b, 0.0..=1.0, background_color.b, move |b| {
+ Message::BackgroundColorChanged(Color {
+ b,
+ ..background_color
+ })
+ })
+ .step(0.01),
+ );
+
+ Row::new()
+ .width(Length::Fill)
+ .height(Length::Fill)
+ .align_items(Align::End)
+ .push(
+ Column::new()
+ .width(Length::Fill)
+ .align_items(Align::End)
+ .push(
+ Column::new()
+ .padding(10)
+ .spacing(10)
+ .push(
+ Text::new("Background color")
+ .color(Color::WHITE),
+ )
+ .push(sliders)
+ .push(
+ Text::new(format!("{:?}", background_color))
+ .size(14)
+ .color(Color::WHITE),
+ ),
+ ),
+ )
+ .into()
+ }
+}
diff --git a/examples/integration_opengl/src/main.rs b/examples/integration_opengl/src/main.rs
new file mode 100644
index 00000000..f80915d2
--- /dev/null
+++ b/examples/integration_opengl/src/main.rs
@@ -0,0 +1,181 @@
+mod controls;
+mod scene;
+
+use controls::Controls;
+use scene::Scene;
+
+use glow;
+use glow::*;
+use iced_glow::{Backend, Renderer, Settings, Viewport};
+use iced_glutin::glutin;
+use iced_glutin::glutin::event::{Event, WindowEvent};
+use iced_glutin::glutin::event_loop::ControlFlow;
+use iced_glutin::{program, Clipboard, Debug, Size};
+use iced_winit::conversion;
+use iced_winit::winit;
+use winit::{dpi::PhysicalPosition, event::ModifiersState};
+
+pub fn main() {
+ env_logger::init();
+ let (gl, event_loop, windowed_context, shader_version) = {
+ let el = glutin::event_loop::EventLoop::new();
+
+ let wb = glutin::window::WindowBuilder::new()
+ .with_title("OpenGL integration example")
+ .with_inner_size(glutin::dpi::LogicalSize::new(1024.0, 768.0));
+
+ let windowed_context = glutin::ContextBuilder::new()
+ .with_vsync(true)
+ .build_windowed(wb, &el)
+ .unwrap();
+
+ unsafe {
+ let windowed_context = windowed_context.make_current().unwrap();
+
+ let gl = glow::Context::from_loader_function(|s| {
+ windowed_context.get_proc_address(s) as *const _
+ });
+
+ // Enable auto-conversion from/to sRGB
+ gl.enable(glow::FRAMEBUFFER_SRGB);
+
+ // Enable alpha blending
+ gl.enable(glow::BLEND);
+ gl.blend_func(glow::SRC_ALPHA, glow::ONE_MINUS_SRC_ALPHA);
+
+ // Disable multisampling by default
+ gl.disable(glow::MULTISAMPLE);
+
+ (gl, el, windowed_context, "#version 410")
+ }
+ };
+
+ let physical_size = windowed_context.window().inner_size();
+ let mut viewport = Viewport::with_physical_size(
+ Size::new(physical_size.width, physical_size.height),
+ windowed_context.window().scale_factor(),
+ );
+
+ let mut cursor_position = PhysicalPosition::new(-1.0, -1.0);
+ let mut modifiers = ModifiersState::default();
+ let mut clipboard = Clipboard::connect(&windowed_context.window());
+
+ let mut renderer = Renderer::new(Backend::new(&gl, Settings::default()));
+
+ let mut debug = Debug::new();
+
+ let controls = Controls::new();
+ let mut state = program::State::new(
+ controls,
+ viewport.logical_size(),
+ conversion::cursor_position(cursor_position, viewport.scale_factor()),
+ &mut renderer,
+ &mut debug,
+ );
+ let mut resized = false;
+
+ event_loop.run(move |event, _, control_flow| {
+ let scene = Scene::new(&gl, &shader_version);
+ *control_flow = ControlFlow::Wait;
+
+ match event {
+ Event::LoopDestroyed => return,
+ Event::WindowEvent { event, .. } => {
+ match event {
+ WindowEvent::CursorMoved { position, .. } => {
+ cursor_position = position;
+ }
+ WindowEvent::ModifiersChanged(new_modifiers) => {
+ modifiers = new_modifiers;
+ }
+ WindowEvent::Resized(physical_size) => {
+ viewport = Viewport::with_physical_size(
+ Size::new(
+ physical_size.width,
+ physical_size.height,
+ ),
+ windowed_context.window().scale_factor(),
+ );
+
+ resized = true;
+ }
+ WindowEvent::CloseRequested => {
+ scene.cleanup(&gl);
+ *control_flow = ControlFlow::Exit
+ }
+ _ => (),
+ }
+
+ // Map window event to iced event
+ if let Some(event) = iced_winit::conversion::window_event(
+ &event,
+ windowed_context.window().scale_factor(),
+ modifiers,
+ ) {
+ state.queue_event(event);
+ }
+ }
+ Event::MainEventsCleared => {
+ // If there are events pending
+ if !state.is_queue_empty() {
+ // We update iced
+ let _ = state.update(
+ viewport.logical_size(),
+ conversion::cursor_position(
+ cursor_position,
+ viewport.scale_factor(),
+ ),
+ &mut renderer,
+ &mut clipboard,
+ &mut debug,
+ );
+
+ // and request a redraw
+ windowed_context.window().request_redraw();
+ }
+ }
+ Event::RedrawRequested(_) => {
+ if resized {
+ let size = windowed_context.window().inner_size();
+
+ unsafe {
+ gl.viewport(
+ 0,
+ 0,
+ size.width as i32,
+ size.height as i32,
+ );
+ }
+
+ resized = false;
+ }
+
+ let program = state.program();
+ {
+ // We clear the frame
+ scene.clear(&gl, program.background_color());
+
+ // Draw the scene
+ scene.draw(&gl);
+ }
+
+ // And then iced on top
+ let mouse_interaction = renderer.backend_mut().draw(
+ &gl,
+ &viewport,
+ state.primitive(),
+ &debug.overlay(),
+ );
+ // Update the mouse cursor
+ windowed_context.window().set_cursor_icon(
+ iced_winit::conversion::mouse_interaction(
+ mouse_interaction,
+ ),
+ );
+
+ windowed_context.swap_buffers().unwrap();
+ }
+ _ => (),
+ }
+ });
+}
diff --git a/examples/integration_opengl/src/scene.rs b/examples/integration_opengl/src/scene.rs
new file mode 100644
index 00000000..ccca0d29
--- /dev/null
+++ b/examples/integration_opengl/src/scene.rs
@@ -0,0 +1,99 @@
+use glow::*;
+use iced_glow::Color;
+
+pub struct Scene {
+ program: glow::Program,
+ vertex_array: glow::VertexArray,
+}
+
+impl Scene {
+ pub fn new(gl: &glow::Context, shader_version: &str) -> Self {
+ unsafe {
+ let vertex_array = gl
+ .create_vertex_array()
+ .expect("Cannot create vertex array");
+ gl.bind_vertex_array(Some(vertex_array));
+
+ let program = gl.create_program().expect("Cannot create program");
+
+ let (vertex_shader_source, fragment_shader_source) = (
+ r#"const vec2 verts[3] = vec2[3](
+ vec2(0.5f, 1.0f),
+ vec2(0.0f, 0.0f),
+ vec2(1.0f, 0.0f)
+ );
+ out vec2 vert;
+ void main() {
+ vert = verts[gl_VertexID];
+ gl_Position = vec4(vert - 0.5, 0.0, 1.0);
+ }"#,
+ r#"precision highp float;
+ in vec2 vert;
+ out vec4 color;
+ void main() {
+ color = vec4(vert, 0.5, 1.0);
+ }"#,
+ );
+
+ let shader_sources = [
+ (glow::VERTEX_SHADER, vertex_shader_source),
+ (glow::FRAGMENT_SHADER, fragment_shader_source),
+ ];
+
+ let mut shaders = Vec::with_capacity(shader_sources.len());
+
+ for (shader_type, shader_source) in shader_sources.iter() {
+ let shader = gl
+ .create_shader(*shader_type)
+ .expect("Cannot create shader");
+ gl.shader_source(
+ shader,
+ &format!("{}\n{}", shader_version, shader_source),
+ );
+ gl.compile_shader(shader);
+ if !gl.get_shader_compile_status(shader) {
+ panic!("{}", gl.get_shader_info_log(shader));
+ }
+ gl.attach_shader(program, shader);
+ shaders.push(shader);
+ }
+
+ gl.link_program(program);
+ if !gl.get_program_link_status(program) {
+ panic!("{}", gl.get_program_info_log(program));
+ }
+
+ for shader in shaders {
+ gl.detach_shader(program, shader);
+ gl.delete_shader(shader);
+ }
+
+ gl.use_program(Some(program));
+ Self {
+ program,
+ vertex_array,
+ }
+ }
+ }
+
+ pub fn clear(&self, gl: &glow::Context, background_color: Color) {
+ let [r, g, b, a] = background_color.into_linear();
+ unsafe {
+ gl.clear_color(r, g, b, a);
+ gl.clear(glow::COLOR_BUFFER_BIT);
+ }
+ }
+
+ pub fn draw(&self, gl: &glow::Context) {
+ unsafe {
+ gl.draw_arrays(glow::TRIANGLES, 0, 3);
+ }
+ }
+
+ pub fn cleanup(&self, gl: &glow::Context) {
+ unsafe {
+ gl.delete_program(self.program);
+ gl.delete_vertex_array(self.vertex_array);
+ }
+ }
+}
diff --git a/examples/integration/Cargo.toml b/examples/integration_wgpu/Cargo.toml
index 4515502f..a088dd1b 100644
--- a/examples/integration/Cargo.toml
+++ b/examples/integration_wgpu/Cargo.toml
@@ -1,5 +1,5 @@
[package]
-name = "integration"
+name = "integration_wgpu"
version = "0.1.0"
authors = ["Héctor Ramón Jiménez <hector0193@gmail.com>"]
edition = "2018"
@@ -7,5 +7,5 @@ publish = false
[dependencies]
iced_winit = { path = "../../winit" }
-iced_wgpu = { path = "../../wgpu" }
+iced_wgpu = { path = "../../wgpu", features = ["spirv"] }
env_logger = "0.8"
diff --git a/examples/integration/README.md b/examples/integration_wgpu/README.md
index d5aabc19..c51c2c65 100644
--- a/examples/integration/README.md
+++ b/examples/integration_wgpu/README.md
@@ -1,6 +1,6 @@
-## Integration
+## `wgpu` integration
-A demonstration of how to integrate Iced in an existing graphical application.
+A demonstration of how to integrate Iced in an existing [`wgpu`] application.
The __[`main`]__ file contains all the code of the example.
@@ -16,3 +16,4 @@ cargo run --package integration
```
[`main`]: src/main.rs
+[`wgpu`]: https://github.com/gfx-rs/wgpu
diff --git a/examples/integration/src/controls.rs b/examples/integration_wgpu/src/controls.rs
index 36ee9b7e..36ee9b7e 100644
--- a/examples/integration/src/controls.rs
+++ b/examples/integration_wgpu/src/controls.rs
diff --git a/examples/integration/src/main.rs b/examples/integration_wgpu/src/main.rs
index b57c73d8..7ef148bc 100644
--- a/examples/integration/src/main.rs
+++ b/examples/integration_wgpu/src/main.rs
@@ -31,10 +31,10 @@ pub fn main() {
let mut clipboard = Clipboard::connect(&window);
// Initialize wgpu
- let instance = wgpu::Instance::new(wgpu::BackendBit::PRIMARY);
+ let instance = wgpu::Instance::new(wgpu::Backends::PRIMARY);
let surface = unsafe { instance.create_surface(&window) };
- let (mut device, queue) = futures::executor::block_on(async {
+ let (format, (mut device, queue)) = futures::executor::block_on(async {
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::HighPerformance,
@@ -43,29 +43,32 @@ pub fn main() {
.await
.expect("Request adapter");
- adapter
- .request_device(
- &wgpu::DeviceDescriptor {
- label: None,
- features: wgpu::Features::empty(),
- limits: wgpu::Limits::default(),
- },
- None,
- )
- .await
- .expect("Request device")
+ (
+ surface
+ .get_preferred_format(&adapter)
+ .expect("Get preferred format"),
+ adapter
+ .request_device(
+ &wgpu::DeviceDescriptor {
+ label: None,
+ features: wgpu::Features::empty(),
+ limits: wgpu::Limits::default(),
+ },
+ None,
+ )
+ .await
+ .expect("Request device"),
+ )
});
- let format = wgpu::TextureFormat::Bgra8UnormSrgb;
-
- let mut swap_chain = {
+ {
let size = window.inner_size();
- device.create_swap_chain(
- &surface,
- &wgpu::SwapChainDescriptor {
- usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
- format: format,
+ surface.configure(
+ &device,
+ &wgpu::SurfaceConfiguration {
+ usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
+ format,
width: size.width,
height: size.height,
present_mode: wgpu::PresentMode::Mailbox,
@@ -85,7 +88,7 @@ pub fn main() {
// Initialize iced
let mut debug = Debug::new();
let mut renderer =
- Renderer::new(Backend::new(&mut device, Settings::default()));
+ Renderer::new(Backend::new(&mut device, Settings::default(), format));
let mut state = program::State::new(
controls,
@@ -155,10 +158,10 @@ pub fn main() {
if resized {
let size = window.inner_size();
- swap_chain = device.create_swap_chain(
- &surface,
- &wgpu::SwapChainDescriptor {
- usage: wgpu::TextureUsage::RENDER_ATTACHMENT,
+ surface.configure(
+ &device,
+ &wgpu::SurfaceConfiguration {
+ usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: format,
width: size.width,
height: size.height,
@@ -169,55 +172,68 @@ pub fn main() {
resized = false;
}
- let frame = swap_chain.get_current_frame().expect("Next frame");
+ match surface.get_current_frame() {
+ Ok(frame) => {
+ let mut encoder = device.create_command_encoder(
+ &wgpu::CommandEncoderDescriptor { label: None },
+ );
+
+ let program = state.program();
+
+ let view = frame.output.texture.create_view(&wgpu::TextureViewDescriptor::default());
+
+ {
+ // We clear the frame
+ let mut render_pass = scene.clear(
+ &view,
+ &mut encoder,
+ program.background_color(),
+ );
+
+ // Draw the scene
+ scene.draw(&mut render_pass);
+ }
+
+ // And then iced on top
+ let mouse_interaction = renderer.backend_mut().draw(
+ &mut device,
+ &mut staging_belt,
+ &mut encoder,
+ &view,
+ &viewport,
+ state.primitive(),
+ &debug.overlay(),
+ );
- let mut encoder = device.create_command_encoder(
- &wgpu::CommandEncoderDescriptor { label: None },
- );
+ // Then we submit the work
+ staging_belt.finish();
+ queue.submit(Some(encoder.finish()));
- let program = state.program();
+ // Update the mouse cursor
+ window.set_cursor_icon(
+ iced_winit::conversion::mouse_interaction(
+ mouse_interaction,
+ ),
+ );
- {
- // We clear the frame
- let mut render_pass = scene.clear(
- &frame.output.view,
- &mut encoder,
- program.background_color(),
- );
+ // And recall staging buffers
+ local_pool
+ .spawner()
+ .spawn(staging_belt.recall())
+ .expect("Recall staging buffers");
- // Draw the scene
- scene.draw(&mut render_pass);
+ local_pool.run_until_stalled();
+ }
+ Err(error) => match error {
+ wgpu::SurfaceError::OutOfMemory => {
+ panic!("Swapchain error: {}. Rendering cannot continue.", error)
+ }
+ _ => {
+ // Try rendering again next frame.
+ window.request_redraw();
+ }
+ },
}
-
- // And then iced on top
- let mouse_interaction = renderer.backend_mut().draw(
- &mut device,
- &mut staging_belt,
- &mut encoder,
- &frame.output.view,
- &viewport,
- state.primitive(),
- &debug.overlay(),
- );
-
- // Then we submit the work
- staging_belt.finish();
- queue.submit(Some(encoder.finish()));
-
- // Update the mouse cursor
- window.set_cursor_icon(
- iced_winit::conversion::mouse_interaction(
- mouse_interaction,
- ),
- );
-
- // And recall staging buffers
- local_pool
- .spawner()
- .spawn(staging_belt.recall())
- .expect("Recall staging buffers");
-
- local_pool.run_until_stalled();
}
_ => {}
}
diff --git a/examples/integration/src/scene.rs b/examples/integration_wgpu/src/scene.rs
index 36c0a41d..0b2b1fcd 100644
--- a/examples/integration/src/scene.rs
+++ b/examples/integration_wgpu/src/scene.rs
@@ -20,8 +20,8 @@ impl Scene {
) -> wgpu::RenderPass<'a> {
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
- color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
- attachment: target,
+ color_attachments: &[wgpu::RenderPassColorAttachment {
+ view: target,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear({
@@ -75,15 +75,16 @@ fn build_pipeline(device: &wgpu::Device) -> wgpu::RenderPipeline {
entry_point: "main",
targets: &[wgpu::ColorTargetState {
format: wgpu::TextureFormat::Bgra8UnormSrgb,
- color_blend: wgpu::BlendState::REPLACE,
- alpha_blend: wgpu::BlendState::REPLACE,
- write_mask: wgpu::ColorWrite::ALL,
+ blend: Some(wgpu::BlendState {
+ color: wgpu::BlendComponent::REPLACE,
+ alpha: wgpu::BlendComponent::REPLACE,
+ }),
+ write_mask: wgpu::ColorWrites::ALL,
}],
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
front_face: wgpu::FrontFace::Ccw,
- cull_mode: wgpu::CullMode::None,
..Default::default()
},
depth_stencil: None,
diff --git a/examples/integration/src/shader/frag.spv b/examples/integration_wgpu/src/shader/frag.spv
index 9d6807c9..9d6807c9 100644
--- a/examples/integration/src/shader/frag.spv
+++ b/examples/integration_wgpu/src/shader/frag.spv
Binary files differ
diff --git a/examples/integration/src/shader/vert.spv b/examples/integration_wgpu/src/shader/vert.spv
index 0cabc9c0..0cabc9c0 100644
--- a/examples/integration/src/shader/vert.spv
+++ b/examples/integration_wgpu/src/shader/vert.spv
Binary files differ
diff --git a/examples/menu/Cargo.toml b/examples/menu/Cargo.toml
new file mode 100644
index 00000000..44597734
--- /dev/null
+++ b/examples/menu/Cargo.toml
@@ -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" } \ No newline at end of file
diff --git a/examples/menu/src/main.rs b/examples/menu/src/main.rs
new file mode 100644
index 00000000..7403713c
--- /dev/null
+++ b/examples/menu/src/main.rs
@@ -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()
+ }
+}
diff --git a/examples/pane_grid/src/main.rs b/examples/pane_grid/src/main.rs
index 4b87a568..3bd8aa25 100644
--- a/examples/pane_grid/src/main.rs
+++ b/examples/pane_grid/src/main.rs
@@ -11,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>,
}
@@ -24,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,
}
@@ -34,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 {
@@ -60,7 +61,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 {
@@ -74,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 {
@@ -106,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);
@@ -113,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);
+ }
+ }
}
}
}
@@ -133,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,
}
})
@@ -143,12 +156,20 @@ 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 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);
let title = Row::with_children(vec![
+ pin_button.into(),
Text::new("Pane").into(),
- Text::new(content.id.to_string())
+ Text::new(pane.content.id.to_string())
.color(if is_focused {
PANE_ID_COLOR_FOCUSED
} else {
@@ -159,12 +180,17 @@ impl Application for Example {
.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(content.view(pane, total_panes))
- .title_bar(title_bar)
- .style(style::Pane { 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)
@@ -212,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,
@@ -220,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 {
@@ -234,6 +282,7 @@ impl Content {
&mut self,
pane: pane_grid::Pane,
total_panes: usize,
+ is_pinned: bool,
) -> Element<Message> {
let Content {
scroll,
@@ -273,7 +322,7 @@ impl Content {
style::Button::Primary,
));
- if total_panes > 1 {
+ if total_panes > 1 && !is_pinned {
controls = controls.push(button(
close,
"Close",
@@ -297,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(
@@ -359,6 +433,8 @@ mod style {
pub enum Button {
Primary,
Destructive,
+ Control,
+ Pin,
}
impl button::StyleSheet for Button {
@@ -368,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 {
@@ -388,6 +466,8 @@ mod style {
a: 0.2,
..active.text_color
}),
+ Button::Control => Some(PANE_ID_COLOR_FOCUSED),
+ Button::Pin => Some(HOVERED),
};
button::Style {
diff --git a/examples/pick_list/src/main.rs b/examples/pick_list/src/main.rs
index 68662602..1eec9791 100644
--- a/examples/pick_list/src/main.rs
+++ b/examples/pick_list/src/main.rs
@@ -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)
diff --git a/examples/pokedex/src/main.rs b/examples/pokedex/src/main.rs
index d2f1bb62..da1d5d5d 100644
--- a/examples/pokedex/src/main.rs
+++ b/examples/pokedex/src/main.rs
@@ -213,7 +213,10 @@ impl Pokemon {
}
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"))]
{
diff --git a/examples/scrollable/Cargo.toml b/examples/scrollable/Cargo.toml
index 12753fb6..08502458 100644
--- a/examples/scrollable/Cargo.toml
+++ b/examples/scrollable/Cargo.toml
@@ -6,4 +6,4 @@ edition = "2018"
publish = false
[dependencies]
-iced = { path = "../.." }
+iced = { path = "../..", features = ["debug"] }
diff --git a/examples/scrollable/src/main.rs b/examples/scrollable/src/main.rs
index 8dd2e20c..3416b83d 100644
--- a/examples/scrollable/src/main.rs
+++ b/examples/scrollable/src/main.rs
@@ -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."));
-
- Container::new(scrollable)
+ .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)),
+ );
+
+ 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,
},
]
}
diff --git a/examples/styling/src/main.rs b/examples/styling/src/main.rs
index 8975fd9a..7bc49281 100644
--- a/examples/styling/src/main.rs
+++ b/examples/styling/src/main.rs
@@ -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 {
diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs
index 7186b950..97415475 100644
--- a/examples/todos/src/main.rs
+++ b/examples/todos/src/main.rs
@@ -265,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(),
};
}
diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs
index e8755d39..1215f83d 100644
--- a/examples/tour/src/main.rs
+++ b/examples/tour/src/main.rs
@@ -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,
diff --git a/examples/url_handler/Cargo.toml b/examples/url_handler/Cargo.toml
new file mode 100644
index 00000000..911b2f25
--- /dev/null
+++ b/examples/url_handler/Cargo.toml
@@ -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" } \ No newline at end of file
diff --git a/examples/url_handler/src/main.rs b/examples/url_handler/src/main.rs
new file mode 100644
index 00000000..f14e5227
--- /dev/null
+++ b/examples/url_handler/src/main.rs
@@ -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()
+ }
+}