Merge branch 'master' into fear/linear-gradients

This commit is contained in:
Bingus 2022-10-07 16:58:45 -07:00 committed by GitHub
commit aabc4e87b5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 257 additions and 12 deletions

View file

@ -72,6 +72,7 @@ members = [
"examples/integration_opengl",
"examples/integration_wgpu",
"examples/modern_art",
"examples/multitouch",
"examples/pane_grid",
"examples/pick_list",
"examples/pokedex",

View file

@ -8,7 +8,7 @@
[![Crates.io](https://img.shields.io/crates/v/iced.svg)](https://crates.io/crates/iced)
[![License](https://img.shields.io/crates/l/iced.svg)](https://github.com/iced-rs/iced/blob/master/LICENSE)
[![Downloads](https://img.shields.io/crates/d/iced.svg)](https://crates.io/crates/iced)
[![Test Status](https://github.com/iced-rs/iced/workflows/Test/badge.svg?event=push)](https://github.com/iced-rs/iced/actions)
[![Test Status](https://img.shields.io/github/workflow/status/iced-rs/iced/Test?event=push&label=test)](https://github.com/iced-rs/iced/actions)
[![Discord Server](https://img.shields.io/discord/628993209984614400?label=&labelColor=6A7EC2&logo=discord&logoColor=ffffff&color=7389D8)](https://discord.gg/3xZJ65GAhd)
A cross-platform GUI library for Rust focused on simplicity and type-safety.

View file

@ -204,6 +204,7 @@ fn view_controls<'a>(
mod grid {
use crate::Preset;
use iced::touch;
use iced::widget::canvas;
use iced::widget::canvas::event::{self, Event};
use iced::widget::canvas::{
@ -423,6 +424,19 @@ mod grid {
};
match event {
Event::Touch(touch::Event::FingerMoved { .. }) => {
let message = {
*interaction = if is_populated {
Interaction::Erasing
} else {
Interaction::Drawing
};
populate.or(unpopulate)
};
(event::Status::Captured, message)
}
Event::Mouse(mouse_event) => match mouse_event {
mouse::Event::ButtonPressed(button) => {
let message = match button {

View file

@ -0,0 +1,12 @@
[package]
name = "multitouch"
version = "0.1.0"
authors = ["Artur Sapek <artur@kraken.com>"]
edition = "2021"
publish = false
[dependencies]
iced = { path = "../..", features = ["canvas", "tokio", "debug"] }
tokio = { version = "1.0", features = ["sync"] }
env_logger = "0.9"
voronator = "0.2"

View file

@ -0,0 +1,199 @@
//! This example shows how to use touch events in `Canvas` to draw
//! a circle around each fingertip. This only works on touch-enabled
//! computers like Microsoft Surface.
use iced::widget::canvas::event;
use iced::widget::canvas::{self, Canvas, Cursor, Geometry, Stroke};
use iced::{
executor, touch, window, Application, Color, Command, Element, Length,
Point, Rectangle, Settings, Subscription, Theme,
};
use std::collections::HashMap;
pub fn main() -> iced::Result {
env_logger::builder().format_timestamp(None).init();
Multitouch::run(Settings {
antialiasing: true,
window: window::Settings {
position: window::Position::Centered,
..window::Settings::default()
},
..Settings::default()
})
}
struct Multitouch {
state: State,
}
#[derive(Debug)]
struct State {
cache: canvas::Cache,
fingers: HashMap<touch::Finger, Point>,
}
impl State {
fn new() -> Self {
Self {
cache: canvas::Cache::new(),
fingers: HashMap::new(),
}
}
}
#[derive(Debug)]
enum Message {
FingerPressed { id: touch::Finger, position: Point },
FingerLifted { id: touch::Finger },
}
impl Application for Multitouch {
type Executor = executor::Default;
type Message = Message;
type Theme = Theme;
type Flags = ();
fn new(_flags: ()) -> (Self, Command<Message>) {
(
Multitouch {
state: State::new(),
},
Command::none(),
)
}
fn title(&self) -> String {
String::from("Multitouch - Iced")
}
fn update(&mut self, message: Message) -> Command<Message> {
match message {
Message::FingerPressed { id, position } => {
self.state.fingers.insert(id, position);
self.state.cache.clear();
}
Message::FingerLifted { id } => {
self.state.fingers.remove(&id);
self.state.cache.clear();
}
}
Command::none()
}
fn subscription(&self) -> Subscription<Message> {
Subscription::none()
}
fn view(&self) -> Element<Message> {
Canvas::new(&self.state)
.width(Length::Fill)
.height(Length::Fill)
.into()
}
}
impl canvas::Program<Message> for State {
type State = ();
fn update(
&self,
_state: &mut Self::State,
event: event::Event,
_bounds: Rectangle,
_cursor: Cursor,
) -> (event::Status, Option<Message>) {
match event {
event::Event::Touch(touch_event) => match touch_event {
touch::Event::FingerPressed { id, position }
| touch::Event::FingerMoved { id, position } => (
event::Status::Captured,
Some(Message::FingerPressed { id, position }),
),
touch::Event::FingerLifted { id, .. }
| touch::Event::FingerLost { id, .. } => (
event::Status::Captured,
Some(Message::FingerLifted { id }),
),
},
_ => (event::Status::Ignored, None),
}
}
fn draw(
&self,
_state: &Self::State,
_theme: &Theme,
bounds: Rectangle,
_cursor: Cursor,
) -> Vec<Geometry> {
let fingerweb = self.cache.draw(bounds.size(), |frame| {
if self.fingers.len() < 2 {
return;
}
// Collect tuples of (id, point);
let mut zones: Vec<(u64, Point)> =
self.fingers.iter().map(|(id, pt)| (id.0, *pt)).collect();
// Sort by ID
zones.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
// Generate sorted list of points
let vpoints: Vec<(f64, f64)> = zones
.iter()
.map(|(_, p)| (f64::from(p.x), f64::from(p.y)))
.collect();
let diagram: voronator::VoronoiDiagram<
voronator::delaunator::Point,
> = voronator::VoronoiDiagram::from_tuple(
&(0.0, 0.0),
&(700.0, 700.0),
&vpoints,
)
.expect("Generate Voronoi diagram");
for (cell, zone) in diagram.cells().iter().zip(zones) {
let mut builder = canvas::path::Builder::new();
for (index, p) in cell.points().iter().enumerate() {
let p = Point::new(p.x as f32, p.y as f32);
match index {
0 => builder.move_to(p),
_ => builder.line_to(p),
}
}
let path = builder.build();
let color_r = (10 % zone.0) as f32 / 20.0;
let color_g = (10 % (zone.0 + 8)) as f32 / 20.0;
let color_b = (10 % (zone.0 + 3)) as f32 / 20.0;
frame.fill(
&path,
Color {
r: color_r,
g: color_g,
b: color_b,
a: 1.0,
},
);
frame.stroke(
&path,
Stroke {
color: Color::BLACK,
width: 3.0,
..Stroke::default()
},
);
}
});
vec![fingerweb]
}
}

View file

@ -170,6 +170,9 @@ where
iced_native::Event::Mouse(mouse_event) => {
Some(Event::Mouse(mouse_event))
}
iced_native::Event::Touch(touch_event) => {
Some(Event::Touch(touch_event))
}
iced_native::Event::Keyboard(keyboard_event) => {
Some(Event::Keyboard(keyboard_event))
}

View file

@ -1,6 +1,7 @@
//! Handle events of a canvas.
use iced_native::keyboard;
use iced_native::mouse;
use iced_native::touch;
pub use iced_native::event::Status;
@ -12,6 +13,9 @@ pub enum Event {
/// A mouse event.
Mouse(mouse::Event),
/// A touch event.
Touch(touch::Event),
/// A keyboard event.
Keyboard(keyboard::Event),
}

View file

@ -230,9 +230,9 @@ where
let mut children = layout.children();
let custom_style = if is_mouse_over {
theme.hovered(self.style)
theme.hovered(self.style, self.is_selected)
} else {
theme.active(self.style)
theme.active(self.style, self.is_selected)
};
{

View file

@ -177,6 +177,7 @@ pub mod mouse;
pub mod overlay;
pub mod settings;
pub mod time;
pub mod touch;
pub mod widget;
pub mod window;

2
src/touch.rs Normal file
View file

@ -0,0 +1,2 @@
//! Listen and react to touch events.
pub use crate::runtime::touch::{Event, Finger};

View file

@ -15,7 +15,7 @@ pub struct Appearance {
pub trait StyleSheet {
type Style: Default + Copy;
fn active(&self, style: Self::Style) -> Appearance;
fn active(&self, style: Self::Style, is_selected: bool) -> Appearance;
fn hovered(&self, style: Self::Style) -> Appearance;
fn hovered(&self, style: Self::Style, is_selected: bool) -> Appearance;
}

View file

@ -415,7 +415,11 @@ impl pick_list::StyleSheet for Theme {
impl radio::StyleSheet for Theme {
type Style = ();
fn active(&self, _style: Self::Style) -> radio::Appearance {
fn active(
&self,
_style: Self::Style,
_is_selected: bool,
) -> radio::Appearance {
let palette = self.extended_palette();
radio::Appearance {
@ -427,8 +431,12 @@ impl radio::StyleSheet for Theme {
}
}
fn hovered(&self, style: Self::Style) -> radio::Appearance {
let active = self.active(style);
fn hovered(
&self,
style: Self::Style,
is_selected: bool,
) -> radio::Appearance {
let active = self.active(style, is_selected);
let palette = self.extended_palette();
radio::Appearance {

View file

@ -2,7 +2,7 @@
use iced_core::Color;
/// The appearance of a toggler.
#[derive(Debug)]
#[derive(Debug, Clone, Copy)]
pub struct Appearance {
pub background: Color,
pub background_border: Option<Color>,

View file

@ -4,9 +4,9 @@
[![License](https://img.shields.io/crates/l/iced_wgpu.svg)](https://github.com/iced-rs/iced/blob/master/LICENSE)
[![Discord Server](https://img.shields.io/discord/628993209984614400?label=&labelColor=6A7EC2&logo=discord&logoColor=ffffff&color=7389D8)](https://discord.gg/3xZJ65GAhd)
`iced_wgpu` is a [`wgpu`] renderer for [`iced_native`]. For now, it is the default renderer of Iced in native platforms.
`iced_wgpu` is a [`wgpu`] renderer for [`iced_native`]. For now, it is the default renderer of Iced on [native platforms].
[`wgpu`] supports most modern graphics backends: Vulkan, Metal, DX11, and DX12 (OpenGL and WebGL are still WIP). Additionally, it will support the incoming [WebGPU API].
[`wgpu`] supports most modern graphics backends: Vulkan, Metal, and DX12 (OpenGL and WebGL are still WIP). Additionally, it will support the incoming [WebGPU API].
Currently, `iced_wgpu` supports the following primitives:
- Text, which is rendered using [`wgpu_glyph`]. No shaping at all.
@ -22,6 +22,7 @@ Currently, `iced_wgpu` supports the following primitives:
[documentation]: https://docs.rs/iced_wgpu
[`iced_native`]: ../native
[`wgpu`]: https://github.com/gfx-rs/wgpu
[native platforms]: https://github.com/gfx-rs/wgpu#supported-platforms
[WebGPU API]: https://gpuweb.github.io/gpuweb/
[`wgpu_glyph`]: https://github.com/hecrj/wgpu_glyph
@ -39,7 +40,7 @@ you want to learn about a specific release, check out [the release list].
## Current limitations
The current implementation is quite naive, it uses:
The current implementation is quite naive; it uses:
- A different pipeline/shader for each primitive
- A very simplistic layer model: every `Clip` primitive will generate new layers