Merge branch 'master' into fear/linear-gradients
This commit is contained in:
commit
921c94162e
26 changed files with 802 additions and 65 deletions
|
|
@ -71,6 +71,7 @@ members = [
|
|||
"examples/game_of_life",
|
||||
"examples/integration_opengl",
|
||||
"examples/integration_wgpu",
|
||||
"examples/lazy",
|
||||
"examples/modern_art",
|
||||
"examples/multitouch",
|
||||
"examples/pane_grid",
|
||||
|
|
|
|||
10
examples/cached/Cargo.toml
Normal file
10
examples/cached/Cargo.toml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "cached"
|
||||
version = "0.1.0"
|
||||
authors = ["Nick Senger <dev@nsenger.com>"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
iced = { path = "../..", features = ["debug"] }
|
||||
iced_lazy = { path = "../../lazy" }
|
||||
139
examples/cached/src/main.rs
Normal file
139
examples/cached/src/main.rs
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
use iced::theme;
|
||||
use iced::widget::{
|
||||
button, column, horizontal_space, row, scrollable, text, text_input,
|
||||
};
|
||||
use iced::{Element, Length, Sandbox, Settings};
|
||||
use iced_lazy::lazy;
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
App::run(Settings::default())
|
||||
}
|
||||
|
||||
struct App {
|
||||
options: HashSet<String>,
|
||||
input: String,
|
||||
order: Order,
|
||||
}
|
||||
|
||||
impl Default for App {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
options: ["Foo", "Bar", "Baz", "Qux", "Corge", "Waldo", "Fred"]
|
||||
.into_iter()
|
||||
.map(ToString::to_string)
|
||||
.collect(),
|
||||
input: Default::default(),
|
||||
order: Order::Ascending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Message {
|
||||
InputChanged(String),
|
||||
ToggleOrder,
|
||||
DeleteOption(String),
|
||||
AddOption(String),
|
||||
}
|
||||
|
||||
impl Sandbox for App {
|
||||
type Message = Message;
|
||||
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
String::from("Cached - Iced")
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) {
|
||||
match message {
|
||||
Message::InputChanged(input) => {
|
||||
self.input = input;
|
||||
}
|
||||
Message::ToggleOrder => {
|
||||
self.order = match self.order {
|
||||
Order::Ascending => Order::Descending,
|
||||
Order::Descending => Order::Ascending,
|
||||
}
|
||||
}
|
||||
Message::AddOption(option) => {
|
||||
self.options.insert(option);
|
||||
self.input.clear();
|
||||
}
|
||||
Message::DeleteOption(option) => {
|
||||
self.options.remove(&option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self) -> Element<Message> {
|
||||
let options = lazy((&self.order, self.options.len()), || {
|
||||
let mut options: Vec<_> = self.options.iter().collect();
|
||||
|
||||
options.sort_by(|a, b| match self.order {
|
||||
Order::Ascending => a.to_lowercase().cmp(&b.to_lowercase()),
|
||||
Order::Descending => b.to_lowercase().cmp(&a.to_lowercase()),
|
||||
});
|
||||
|
||||
column(
|
||||
options
|
||||
.into_iter()
|
||||
.map(|option| {
|
||||
row![
|
||||
text(option),
|
||||
horizontal_space(Length::Fill),
|
||||
button("Delete")
|
||||
.on_press(Message::DeleteOption(
|
||||
option.to_string(),
|
||||
),)
|
||||
.style(theme::Button::Destructive)
|
||||
]
|
||||
.into()
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
.spacing(10)
|
||||
});
|
||||
|
||||
column![
|
||||
scrollable(options).height(Length::Fill),
|
||||
row![
|
||||
text_input(
|
||||
"Add a new option",
|
||||
&self.input,
|
||||
Message::InputChanged,
|
||||
)
|
||||
.on_submit(Message::AddOption(self.input.clone())),
|
||||
button(text(format!("Toggle Order ({})", self.order)))
|
||||
.on_press(Message::ToggleOrder)
|
||||
]
|
||||
.spacing(10)
|
||||
]
|
||||
.spacing(20)
|
||||
.padding(20)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
enum Order {
|
||||
Ascending,
|
||||
Descending,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Order {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
Self::Ascending => "Ascending",
|
||||
Self::Descending => "Descending",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -119,6 +119,7 @@ pub fn main() {
|
|||
width: physical_size.width,
|
||||
height: physical_size.height,
|
||||
present_mode: wgpu::PresentMode::AutoVsync,
|
||||
alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
||||
},
|
||||
);
|
||||
|
||||
|
|
@ -213,6 +214,7 @@ pub fn main() {
|
|||
width: size.width,
|
||||
height: size.height,
|
||||
present_mode: wgpu::PresentMode::AutoVsync,
|
||||
alpha_mode: wgpu::CompositeAlphaMode::Auto
|
||||
},
|
||||
);
|
||||
|
||||
|
|
|
|||
10
examples/lazy/Cargo.toml
Normal file
10
examples/lazy/Cargo.toml
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
[package]
|
||||
name = "lazy"
|
||||
version = "0.1.0"
|
||||
authors = ["Nick Senger <dev@nsenger.com>"]
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
iced = { path = "../..", features = ["debug"] }
|
||||
iced_lazy = { path = "../../lazy" }
|
||||
139
examples/lazy/src/main.rs
Normal file
139
examples/lazy/src/main.rs
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
use iced::theme;
|
||||
use iced::widget::{
|
||||
button, column, horizontal_space, row, scrollable, text, text_input,
|
||||
};
|
||||
use iced::{Element, Length, Sandbox, Settings};
|
||||
use iced_lazy::lazy;
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
App::run(Settings::default())
|
||||
}
|
||||
|
||||
struct App {
|
||||
options: HashSet<String>,
|
||||
input: String,
|
||||
order: Order,
|
||||
}
|
||||
|
||||
impl Default for App {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
options: ["Foo", "Bar", "Baz", "Qux", "Corge", "Waldo", "Fred"]
|
||||
.into_iter()
|
||||
.map(ToString::to_string)
|
||||
.collect(),
|
||||
input: Default::default(),
|
||||
order: Order::Ascending,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Message {
|
||||
InputChanged(String),
|
||||
ToggleOrder,
|
||||
DeleteOption(String),
|
||||
AddOption(String),
|
||||
}
|
||||
|
||||
impl Sandbox for App {
|
||||
type Message = Message;
|
||||
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn title(&self) -> String {
|
||||
String::from("Cached - Iced")
|
||||
}
|
||||
|
||||
fn update(&mut self, message: Message) {
|
||||
match message {
|
||||
Message::InputChanged(input) => {
|
||||
self.input = input;
|
||||
}
|
||||
Message::ToggleOrder => {
|
||||
self.order = match self.order {
|
||||
Order::Ascending => Order::Descending,
|
||||
Order::Descending => Order::Ascending,
|
||||
}
|
||||
}
|
||||
Message::AddOption(option) => {
|
||||
self.options.insert(option);
|
||||
self.input.clear();
|
||||
}
|
||||
Message::DeleteOption(option) => {
|
||||
self.options.remove(&option);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn view(&self) -> Element<Message> {
|
||||
let options = lazy((&self.order, self.options.len()), || {
|
||||
let mut options: Vec<_> = self.options.iter().collect();
|
||||
|
||||
options.sort_by(|a, b| match self.order {
|
||||
Order::Ascending => a.to_lowercase().cmp(&b.to_lowercase()),
|
||||
Order::Descending => b.to_lowercase().cmp(&a.to_lowercase()),
|
||||
});
|
||||
|
||||
column(
|
||||
options
|
||||
.into_iter()
|
||||
.map(|option| {
|
||||
row![
|
||||
text(option),
|
||||
horizontal_space(Length::Fill),
|
||||
button("Delete")
|
||||
.on_press(Message::DeleteOption(
|
||||
option.to_string(),
|
||||
),)
|
||||
.style(theme::Button::Destructive)
|
||||
]
|
||||
.into()
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
.spacing(10)
|
||||
});
|
||||
|
||||
column![
|
||||
scrollable(options).height(Length::Fill),
|
||||
row![
|
||||
text_input(
|
||||
"Add a new option",
|
||||
&self.input,
|
||||
Message::InputChanged,
|
||||
)
|
||||
.on_submit(Message::AddOption(self.input.clone())),
|
||||
button(text(format!("Toggle Order ({})", self.order)))
|
||||
.on_press(Message::ToggleOrder)
|
||||
]
|
||||
.spacing(10)
|
||||
]
|
||||
.spacing(20)
|
||||
.padding(20)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash)]
|
||||
enum Order {
|
||||
Ascending,
|
||||
Descending,
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Order {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
match self {
|
||||
Self::Ascending => "Ascending",
|
||||
Self::Descending => "Descending",
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@ publish = false
|
|||
iced = { path = "../..", features = ["async-std", "debug"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
lazy_static = "1.4"
|
||||
once_cell = "1.15"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
async-std = "1.0"
|
||||
|
|
|
|||
|
|
@ -11,12 +11,10 @@ use iced::window;
|
|||
use iced::{Application, Element};
|
||||
use iced::{Color, Command, Font, Length, Settings, Subscription};
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
lazy_static! {
|
||||
static ref INPUT_ID: text_input::Id = text_input::Id::unique();
|
||||
}
|
||||
static INPUT_ID: Lazy<text_input::Id> = Lazy::new(text_input::Id::unique);
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
Todos::run(Settings {
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ publish = false
|
|||
iced = { path = "../..", features = ["tokio", "debug"] }
|
||||
iced_native = { path = "../../native" }
|
||||
iced_futures = { path = "../../futures" }
|
||||
lazy_static = "1.4"
|
||||
once_cell = "1.15"
|
||||
|
||||
[dependencies.async-tungstenite]
|
||||
version = "0.16"
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use iced::widget::{
|
|||
use iced::{
|
||||
Application, Color, Command, Element, Length, Settings, Subscription, Theme,
|
||||
};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
WebSocket::run(Settings::default())
|
||||
|
|
@ -165,6 +166,4 @@ impl Default for State {
|
|||
}
|
||||
}
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
static ref MESSAGE_LOG: scrollable::Id = scrollable::Id::unique();
|
||||
}
|
||||
static MESSAGE_LOG: Lazy<scrollable::Id> = Lazy::new(scrollable::Id::unique);
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ path = "../native"
|
|||
[dependencies.iced_winit]
|
||||
version = "0.4"
|
||||
path = "../winit"
|
||||
features = ["application"]
|
||||
|
||||
[dependencies.iced_graphics]
|
||||
version = "0.3"
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ opengl = []
|
|||
|
||||
[dependencies]
|
||||
glam = "0.21.3"
|
||||
raw-window-handle = "0.4"
|
||||
raw-window-handle = "0.5"
|
||||
thiserror = "1.0"
|
||||
|
||||
[dependencies.bytemuck]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
//! surfaces.
|
||||
use crate::{Color, Error, Viewport};
|
||||
|
||||
use raw_window_handle::HasRawWindowHandle;
|
||||
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
||||
use thiserror::Error;
|
||||
|
||||
/// A graphics compositor that can draw to windows.
|
||||
|
|
@ -17,7 +17,7 @@ pub trait Compositor: Sized {
|
|||
type Surface;
|
||||
|
||||
/// Creates a new [`Compositor`].
|
||||
fn new<W: HasRawWindowHandle>(
|
||||
fn new<W: HasRawWindowHandle + HasRawDisplayHandle>(
|
||||
settings: Self::Settings,
|
||||
compatible_window: Option<&W>,
|
||||
) -> Result<(Self, Self::Renderer), Error>;
|
||||
|
|
@ -25,7 +25,7 @@ pub trait Compositor: Sized {
|
|||
/// Crates a new [`Surface`] for the given window.
|
||||
///
|
||||
/// [`Surface`]: Self::Surface
|
||||
fn create_surface<W: HasRawWindowHandle>(
|
||||
fn create_surface<W: HasRawWindowHandle + HasRawDisplayHandle>(
|
||||
&mut self,
|
||||
window: &W,
|
||||
) -> Self::Surface;
|
||||
|
|
|
|||
362
lazy/src/lazy.rs
Normal file
362
lazy/src/lazy.rs
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
use iced_native::event;
|
||||
use iced_native::layout::{self, Layout};
|
||||
use iced_native::mouse;
|
||||
use iced_native::overlay;
|
||||
use iced_native::renderer;
|
||||
use iced_native::widget::tree::{self, Tree};
|
||||
use iced_native::widget::{self, Widget};
|
||||
use iced_native::Element;
|
||||
use iced_native::{Clipboard, Hasher, Length, Point, Rectangle, Shell, Size};
|
||||
|
||||
use ouroboros::self_referencing;
|
||||
use std::cell::{Ref, RefCell, RefMut};
|
||||
use std::hash::{Hash, Hasher as H};
|
||||
use std::marker::PhantomData;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Lazy<'a, Message, Renderer, Dependency, View> {
|
||||
dependency: Dependency,
|
||||
view: Box<dyn Fn() -> View + 'a>,
|
||||
element: RefCell<Option<Rc<RefCell<Element<'static, Message, Renderer>>>>>,
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer, Dependency, View>
|
||||
Lazy<'a, Message, Renderer, Dependency, View>
|
||||
where
|
||||
Dependency: Hash + 'a,
|
||||
View: Into<Element<'static, Message, Renderer>>,
|
||||
{
|
||||
pub fn new(dependency: Dependency, view: impl Fn() -> View + 'a) -> Self {
|
||||
Self {
|
||||
dependency,
|
||||
view: Box::new(view),
|
||||
element: RefCell::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
fn with_element<T>(
|
||||
&self,
|
||||
f: impl FnOnce(Ref<Element<Message, Renderer>>) -> T,
|
||||
) -> T {
|
||||
f(self.element.borrow().as_ref().unwrap().borrow())
|
||||
}
|
||||
|
||||
fn with_element_mut<T>(
|
||||
&self,
|
||||
f: impl FnOnce(RefMut<Element<Message, Renderer>>) -> T,
|
||||
) -> T {
|
||||
f(self.element.borrow().as_ref().unwrap().borrow_mut())
|
||||
}
|
||||
}
|
||||
|
||||
struct Internal<Message, Renderer> {
|
||||
element: Rc<RefCell<Element<'static, Message, Renderer>>>,
|
||||
hash: u64,
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer, Dependency, View> Widget<Message, Renderer>
|
||||
for Lazy<'a, Message, Renderer, Dependency, View>
|
||||
where
|
||||
View: Into<Element<'static, Message, Renderer>> + 'static,
|
||||
Dependency: Hash + 'a,
|
||||
Message: 'static,
|
||||
Renderer: iced_native::Renderer + 'static,
|
||||
{
|
||||
fn tag(&self) -> tree::Tag {
|
||||
struct Tag<T>(T);
|
||||
tree::Tag::of::<Tag<View>>()
|
||||
}
|
||||
|
||||
fn state(&self) -> tree::State {
|
||||
let mut hasher = Hasher::default();
|
||||
self.dependency.hash(&mut hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
let element = Rc::new(RefCell::new((self.view)().into()));
|
||||
|
||||
(*self.element.borrow_mut()) = Some(element.clone());
|
||||
|
||||
tree::State::new(Internal { element, hash })
|
||||
}
|
||||
|
||||
fn children(&self) -> Vec<Tree> {
|
||||
vec![Tree::new(
|
||||
self.element.borrow().as_ref().unwrap().borrow().as_widget(),
|
||||
)]
|
||||
}
|
||||
|
||||
fn diff(&self, tree: &mut Tree) {
|
||||
let current = tree.state.downcast_mut::<Internal<Message, Renderer>>();
|
||||
|
||||
let mut hasher = Hasher::default();
|
||||
self.dependency.hash(&mut hasher);
|
||||
let new_hash = hasher.finish();
|
||||
|
||||
if current.hash != new_hash {
|
||||
current.hash = new_hash;
|
||||
|
||||
let element = (self.view)().into();
|
||||
current.element = Rc::new(RefCell::new(element));
|
||||
|
||||
(*self.element.borrow_mut()) = Some(current.element.clone());
|
||||
tree.diff_children(std::slice::from_ref(
|
||||
&self.element.borrow().as_ref().unwrap().borrow().as_widget(),
|
||||
));
|
||||
} else {
|
||||
(*self.element.borrow_mut()) = Some(current.element.clone());
|
||||
}
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
self.with_element(|element| element.as_widget().width())
|
||||
}
|
||||
|
||||
fn height(&self) -> Length {
|
||||
self.with_element(|element| element.as_widget().height())
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
self.with_element(|element| {
|
||||
element.as_widget().layout(renderer, limits)
|
||||
})
|
||||
}
|
||||
|
||||
fn operate(
|
||||
&self,
|
||||
tree: &mut Tree,
|
||||
layout: Layout<'_>,
|
||||
operation: &mut dyn widget::Operation<Message>,
|
||||
) {
|
||||
self.with_element(|element| {
|
||||
element.as_widget().operate(
|
||||
&mut tree.children[0],
|
||||
layout,
|
||||
operation,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
event: iced_native::Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
renderer: &Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
self.with_element_mut(|mut element| {
|
||||
element.as_widget_mut().on_event(
|
||||
&mut tree.children[0],
|
||||
event,
|
||||
layout,
|
||||
cursor_position,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
self.with_element(|element| {
|
||||
element.as_widget().mouse_interaction(
|
||||
&tree.children[0],
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
renderer,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
) {
|
||||
self.with_element(|element| {
|
||||
element.as_widget().draw(
|
||||
&tree.children[0],
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'_, Message, Renderer>> {
|
||||
let overlay = OverlayBuilder {
|
||||
cached: self,
|
||||
tree: &mut tree.children[0],
|
||||
types: PhantomData,
|
||||
element_ref_builder: |cached| cached.element.borrow(),
|
||||
element_builder: |element_ref| {
|
||||
element_ref.as_ref().unwrap().borrow()
|
||||
},
|
||||
overlay_builder: |element, tree| {
|
||||
element.as_widget().overlay(tree, layout, renderer)
|
||||
},
|
||||
}
|
||||
.build();
|
||||
|
||||
let has_overlay = overlay.with_overlay(|overlay| {
|
||||
overlay.as_ref().map(overlay::Element::position)
|
||||
});
|
||||
|
||||
has_overlay
|
||||
.map(|position| overlay::Element::new(position, Box::new(overlay)))
|
||||
}
|
||||
}
|
||||
|
||||
#[self_referencing]
|
||||
struct Overlay<'a, 'b, Message, Renderer, Dependency, View> {
|
||||
cached: &'a Lazy<'b, Message, Renderer, Dependency, View>,
|
||||
tree: &'a mut Tree,
|
||||
types: PhantomData<(Message, Dependency, View)>,
|
||||
|
||||
#[borrows(cached)]
|
||||
#[covariant]
|
||||
element_ref:
|
||||
Ref<'this, Option<Rc<RefCell<Element<'static, Message, Renderer>>>>>,
|
||||
|
||||
#[borrows(element_ref)]
|
||||
#[covariant]
|
||||
element: Ref<'this, Element<'static, Message, Renderer>>,
|
||||
|
||||
#[borrows(element, mut tree)]
|
||||
#[covariant]
|
||||
overlay: Option<overlay::Element<'this, Message, Renderer>>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, Message, Renderer, Dependency, View>
|
||||
Overlay<'a, 'b, Message, Renderer, Dependency, View>
|
||||
{
|
||||
fn with_overlay_maybe<T>(
|
||||
&self,
|
||||
f: impl FnOnce(&overlay::Element<'_, Message, Renderer>) -> T,
|
||||
) -> Option<T> {
|
||||
self.borrow_overlay().as_ref().map(f)
|
||||
}
|
||||
|
||||
fn with_overlay_mut_maybe<T>(
|
||||
&mut self,
|
||||
f: impl FnOnce(&mut overlay::Element<'_, Message, Renderer>) -> T,
|
||||
) -> Option<T> {
|
||||
self.with_overlay_mut(|overlay| overlay.as_mut().map(f))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, Message, Renderer, Dependency, View>
|
||||
overlay::Overlay<Message, Renderer>
|
||||
for Overlay<'a, 'b, Message, Renderer, Dependency, View>
|
||||
where
|
||||
Renderer: iced_native::Renderer,
|
||||
{
|
||||
fn layout(
|
||||
&self,
|
||||
renderer: &Renderer,
|
||||
bounds: Size,
|
||||
position: Point,
|
||||
) -> layout::Node {
|
||||
self.with_overlay_maybe(|overlay| {
|
||||
let vector = position - overlay.position();
|
||||
|
||||
overlay.layout(renderer, bounds).translate(vector)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
renderer: &mut Renderer,
|
||||
theme: &Renderer::Theme,
|
||||
style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
) {
|
||||
let _ = self.with_overlay_maybe(|overlay| {
|
||||
overlay.draw(renderer, theme, style, layout, cursor_position);
|
||||
});
|
||||
}
|
||||
|
||||
fn mouse_interaction(
|
||||
&self,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
renderer: &Renderer,
|
||||
) -> mouse::Interaction {
|
||||
self.with_overlay_maybe(|overlay| {
|
||||
overlay.mouse_interaction(
|
||||
layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
renderer,
|
||||
)
|
||||
})
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: iced_native::Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
renderer: &Renderer,
|
||||
clipboard: &mut dyn Clipboard,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
) -> event::Status {
|
||||
self.with_overlay_mut_maybe(|overlay| {
|
||||
overlay.on_event(
|
||||
event,
|
||||
layout,
|
||||
cursor_position,
|
||||
renderer,
|
||||
clipboard,
|
||||
shell,
|
||||
)
|
||||
})
|
||||
.unwrap_or(iced_native::event::Status::Ignored)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer, Dependency, View>
|
||||
From<Lazy<'a, Message, Renderer, Dependency, View>>
|
||||
for Element<'a, Message, Renderer>
|
||||
where
|
||||
View: Into<Element<'static, Message, Renderer>> + 'static,
|
||||
Renderer: iced_native::Renderer + 'static,
|
||||
Message: 'static,
|
||||
Dependency: Hash + 'a,
|
||||
{
|
||||
fn from(lazy: Lazy<'a, Message, Renderer, Dependency, View>) -> Self {
|
||||
Self::new(lazy)
|
||||
}
|
||||
}
|
||||
|
|
@ -17,15 +17,30 @@
|
|||
clippy::type_complexity
|
||||
)]
|
||||
#![cfg_attr(docsrs, feature(doc_cfg))]
|
||||
mod lazy;
|
||||
|
||||
pub mod component;
|
||||
pub mod responsive;
|
||||
|
||||
pub use component::Component;
|
||||
pub use lazy::Lazy;
|
||||
pub use responsive::Responsive;
|
||||
|
||||
mod cache;
|
||||
|
||||
use iced_native::{Element, Size};
|
||||
use std::hash::Hash;
|
||||
|
||||
pub fn lazy<'a, Message, Renderer, Dependency, View>(
|
||||
dependency: Dependency,
|
||||
view: impl Fn() -> View + 'a,
|
||||
) -> Lazy<'a, Message, Renderer, Dependency, View>
|
||||
where
|
||||
Dependency: Hash + 'a,
|
||||
View: Into<Element<'static, Message, Renderer>>,
|
||||
{
|
||||
Lazy::new(dependency, view)
|
||||
}
|
||||
|
||||
/// Turns an implementor of [`Component`] into an [`Element`] that can be
|
||||
/// embedded in any application.
|
||||
|
|
|
|||
|
|
@ -341,6 +341,7 @@ where
|
|||
cursor_position,
|
||||
viewport,
|
||||
renderer,
|
||||
self.on_drag.is_some(),
|
||||
)
|
||||
})
|
||||
.max()
|
||||
|
|
@ -648,7 +649,7 @@ pub fn mouse_interaction(
|
|||
resize_leeway: Option<u16>,
|
||||
) -> Option<mouse::Interaction> {
|
||||
if action.picked_pane().is_some() {
|
||||
return Some(mouse::Interaction::Grab);
|
||||
return Some(mouse::Interaction::Grabbing);
|
||||
}
|
||||
|
||||
let resize_axis =
|
||||
|
|
@ -756,27 +757,12 @@ pub fn draw<Renderer, T>(
|
|||
cursor_position
|
||||
};
|
||||
|
||||
let mut render_picked_pane = None;
|
||||
|
||||
for ((id, pane), layout) in elements.zip(layout.children()) {
|
||||
match picked_pane {
|
||||
Some((dragging, origin)) if id == dragging => {
|
||||
let bounds = layout.bounds();
|
||||
|
||||
renderer.with_translation(
|
||||
cursor_position
|
||||
- Point::new(bounds.x + origin.x, bounds.y + origin.y),
|
||||
|renderer| {
|
||||
renderer.with_layer(bounds, |renderer| {
|
||||
draw_pane(
|
||||
pane,
|
||||
renderer,
|
||||
default_style,
|
||||
layout,
|
||||
pane_cursor_position,
|
||||
viewport,
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
render_picked_pane = Some((pane, origin, layout));
|
||||
}
|
||||
_ => {
|
||||
draw_pane(
|
||||
|
|
@ -791,6 +777,28 @@ pub fn draw<Renderer, T>(
|
|||
}
|
||||
}
|
||||
|
||||
// Render picked pane last
|
||||
if let Some((pane, origin, layout)) = render_picked_pane {
|
||||
let bounds = layout.bounds();
|
||||
|
||||
renderer.with_translation(
|
||||
cursor_position
|
||||
- Point::new(bounds.x + origin.x, bounds.y + origin.y),
|
||||
|renderer| {
|
||||
renderer.with_layer(bounds, |renderer| {
|
||||
draw_pane(
|
||||
pane,
|
||||
renderer,
|
||||
default_style,
|
||||
layout,
|
||||
pane_cursor_position,
|
||||
viewport,
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
if let Some((axis, split_region, is_picked)) = picked_split {
|
||||
let highlight = if is_picked {
|
||||
theme.picked_split(style)
|
||||
|
|
|
|||
|
|
@ -115,6 +115,16 @@ where
|
|||
|
||||
let show_controls = bounds.contains(cursor_position);
|
||||
|
||||
self.body.as_widget().draw(
|
||||
&tree.children[0],
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
body_layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
);
|
||||
|
||||
title_bar.draw(
|
||||
&tree.children[1],
|
||||
renderer,
|
||||
|
|
@ -125,16 +135,6 @@ where
|
|||
viewport,
|
||||
show_controls,
|
||||
);
|
||||
|
||||
self.body.as_widget().draw(
|
||||
&tree.children[0],
|
||||
renderer,
|
||||
theme,
|
||||
style,
|
||||
body_layout,
|
||||
cursor_position,
|
||||
viewport,
|
||||
);
|
||||
} else {
|
||||
self.body.as_widget().draw(
|
||||
&tree.children[0],
|
||||
|
|
@ -238,6 +238,7 @@ where
|
|||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
renderer: &Renderer,
|
||||
drag_enabled: bool,
|
||||
) -> mouse::Interaction {
|
||||
let (body_layout, title_bar_interaction) =
|
||||
if let Some(title_bar) = &self.title_bar {
|
||||
|
|
@ -247,7 +248,7 @@ where
|
|||
let is_over_pick_area = title_bar
|
||||
.is_over_pick_area(title_bar_layout, cursor_position);
|
||||
|
||||
if is_over_pick_area {
|
||||
if is_over_pick_area && drag_enabled {
|
||||
return mouse::Interaction::Grab;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -348,9 +348,9 @@ where
|
|||
let state = state();
|
||||
|
||||
let event_status = if state.is_open {
|
||||
// TODO: Encode cursor availability in the type system
|
||||
state.is_open =
|
||||
cursor_position.x < 0.0 || cursor_position.y < 0.0;
|
||||
// Event wasn't processed by overlay, so cursor was clicked either outside it's
|
||||
// bounds or on the drop-down, either way we close the overlay.
|
||||
state.is_open = false;
|
||||
|
||||
event::Status::Captured
|
||||
} else if layout.bounds().contains(cursor_position) {
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ where
|
|||
is_secure: false,
|
||||
font: Default::default(),
|
||||
width: Length::Fill,
|
||||
padding: Padding::ZERO,
|
||||
padding: Padding::new(5),
|
||||
size: None,
|
||||
on_change: Box::new(on_change),
|
||||
on_paste: None,
|
||||
|
|
@ -712,14 +712,14 @@ where
|
|||
}
|
||||
|
||||
return event::Status::Captured;
|
||||
} else {
|
||||
state.is_pasting = None;
|
||||
}
|
||||
}
|
||||
Event::Keyboard(keyboard::Event::ModifiersChanged(modifiers)) => {
|
||||
let state = state();
|
||||
|
||||
if state.is_focused {
|
||||
state.keyboard_modifiers = modifiers;
|
||||
}
|
||||
state.keyboard_modifiers = modifiers;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,12 @@ use std::fmt;
|
|||
|
||||
/// An operation to be performed on some window.
|
||||
pub enum Action<T> {
|
||||
/// Moves the window with the left mouse button until the button is
|
||||
/// released.
|
||||
///
|
||||
/// There’s no guarantee that this will work unless the left mouse
|
||||
/// button was pressed immediately before this function is called.
|
||||
Drag,
|
||||
/// Resize the window.
|
||||
Resize {
|
||||
/// The new logical width of the window
|
||||
|
|
@ -12,6 +18,10 @@ pub enum Action<T> {
|
|||
/// The new logical height of the window
|
||||
height: u32,
|
||||
},
|
||||
/// Sets the window to maximized or back
|
||||
Maximize(bool),
|
||||
/// Set the window to minimized or back
|
||||
Minimize(bool),
|
||||
/// Move the window.
|
||||
///
|
||||
/// Unsupported on Wayland.
|
||||
|
|
@ -23,6 +33,8 @@ pub enum Action<T> {
|
|||
},
|
||||
/// Set the [`Mode`] of the window.
|
||||
SetMode(Mode),
|
||||
/// Sets the window to maximized or back
|
||||
ToggleMaximize,
|
||||
/// Fetch the current [`Mode`] of the window.
|
||||
FetchMode(Box<dyn FnOnce(Mode) -> T + 'static>),
|
||||
}
|
||||
|
|
@ -37,9 +49,13 @@ impl<T> Action<T> {
|
|||
T: 'static,
|
||||
{
|
||||
match self {
|
||||
Self::Drag => Action::Drag,
|
||||
Self::Resize { width, height } => Action::Resize { width, height },
|
||||
Self::Maximize(bool) => Action::Maximize(bool),
|
||||
Self::Minimize(bool) => Action::Minimize(bool),
|
||||
Self::Move { x, y } => Action::Move { x, y },
|
||||
Self::SetMode(mode) => Action::SetMode(mode),
|
||||
Self::ToggleMaximize => Action::ToggleMaximize,
|
||||
Self::FetchMode(o) => Action::FetchMode(Box::new(move |s| f(o(s)))),
|
||||
}
|
||||
}
|
||||
|
|
@ -48,15 +64,19 @@ impl<T> Action<T> {
|
|||
impl<T> fmt::Debug for Action<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Drag => write!(f, "Action::Drag"),
|
||||
Self::Resize { width, height } => write!(
|
||||
f,
|
||||
"Action::Resize {{ widget: {}, height: {} }}",
|
||||
width, height
|
||||
),
|
||||
Self::Maximize(value) => write!(f, "Action::Maximize({})", value),
|
||||
Self::Minimize(value) => write!(f, "Action::Minimize({}", value),
|
||||
Self::Move { x, y } => {
|
||||
write!(f, "Action::Move {{ x: {}, y: {} }}", x, y)
|
||||
}
|
||||
Self::SetMode(mode) => write!(f, "Action::SetMode({:?})", mode),
|
||||
Self::ToggleMaximize => write!(f, "Action::ToggleMaximize"),
|
||||
Self::FetchMode(_) => write!(f, "Action::FetchMode"),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,5 +18,5 @@ features = ["palette"]
|
|||
[dependencies.palette]
|
||||
version = "0.6"
|
||||
|
||||
[dependencies.lazy_static]
|
||||
version = "1.4"
|
||||
[dependencies.once_cell]
|
||||
version = "1.15"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use iced_core::Color;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use once_cell::sync::Lazy;
|
||||
use palette::{FromColor, Hsl, Mix, RelativeContrast, Srgb};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
|
|
@ -66,11 +66,10 @@ pub struct Extended {
|
|||
pub danger: Danger,
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref EXTENDED_LIGHT: Extended =
|
||||
Extended::generate(Palette::LIGHT);
|
||||
pub static ref EXTENDED_DARK: Extended = Extended::generate(Palette::DARK);
|
||||
}
|
||||
pub static EXTENDED_LIGHT: Lazy<Extended> =
|
||||
Lazy::new(|| Extended::generate(Palette::LIGHT));
|
||||
pub static EXTENDED_DARK: Lazy<Extended> =
|
||||
Lazy::new(|| Extended::generate(Palette::DARK));
|
||||
|
||||
impl Extended {
|
||||
pub fn generate(palette: Palette) -> Self {
|
||||
|
|
|
|||
|
|
@ -28,10 +28,10 @@ spirv = ["wgpu/spirv"]
|
|||
webgl = ["wgpu/webgl"]
|
||||
|
||||
[dependencies]
|
||||
wgpu = "0.13"
|
||||
wgpu_glyph = "0.17"
|
||||
wgpu = "0.14"
|
||||
wgpu_glyph = "0.18"
|
||||
glyph_brush = "0.7"
|
||||
raw-window-handle = "0.4"
|
||||
raw-window-handle = "0.5"
|
||||
log = "0.4"
|
||||
guillotiere = "0.6"
|
||||
futures = "0.3"
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use futures::stream::{self, StreamExt};
|
|||
|
||||
use iced_graphics::compositor;
|
||||
use iced_native::futures;
|
||||
use raw_window_handle::HasRawWindowHandle;
|
||||
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
|
|
@ -27,7 +27,7 @@ impl<Theme> Compositor<Theme> {
|
|||
/// Requests a new [`Compositor`] with the given [`Settings`].
|
||||
///
|
||||
/// Returns `None` if no compatible graphics adapter could be found.
|
||||
pub async fn request<W: HasRawWindowHandle>(
|
||||
pub async fn request<W: HasRawWindowHandle + HasRawDisplayHandle>(
|
||||
settings: Settings,
|
||||
compatible_window: Option<&W>,
|
||||
) -> Option<Self> {
|
||||
|
|
@ -123,7 +123,7 @@ impl<Theme> iced_graphics::window::Compositor for Compositor<Theme> {
|
|||
type Renderer = Renderer<Theme>;
|
||||
type Surface = wgpu::Surface;
|
||||
|
||||
fn new<W: HasRawWindowHandle>(
|
||||
fn new<W: HasRawWindowHandle + HasRawDisplayHandle>(
|
||||
settings: Self::Settings,
|
||||
compatible_window: Option<&W>,
|
||||
) -> Result<(Self, Self::Renderer), Error> {
|
||||
|
|
@ -138,7 +138,7 @@ impl<Theme> iced_graphics::window::Compositor for Compositor<Theme> {
|
|||
Ok((compositor, Renderer::new(backend)))
|
||||
}
|
||||
|
||||
fn create_surface<W: HasRawWindowHandle>(
|
||||
fn create_surface<W: HasRawWindowHandle + HasRawDisplayHandle>(
|
||||
&mut self,
|
||||
window: &W,
|
||||
) -> wgpu::Surface {
|
||||
|
|
@ -162,6 +162,7 @@ impl<Theme> iced_graphics::window::Compositor for Compositor<Theme> {
|
|||
present_mode: self.settings.present_mode,
|
||||
width,
|
||||
height,
|
||||
alpha_mode: wgpu::CompositeAlphaMode::Auto,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -615,12 +615,21 @@ pub fn run_command<A, E>(
|
|||
}
|
||||
},
|
||||
command::Action::Window(action) => match action {
|
||||
window::Action::Drag => {
|
||||
let _res = window.drag_window();
|
||||
}
|
||||
window::Action::Resize { width, height } => {
|
||||
window.set_inner_size(winit::dpi::LogicalSize {
|
||||
width,
|
||||
height,
|
||||
});
|
||||
}
|
||||
window::Action::Maximize(value) => {
|
||||
window.set_maximized(value);
|
||||
}
|
||||
window::Action::Minimize(value) => {
|
||||
window.set_minimized(value);
|
||||
}
|
||||
window::Action::Move { x, y } => {
|
||||
window.set_outer_position(winit::dpi::LogicalPosition {
|
||||
x,
|
||||
|
|
@ -634,6 +643,9 @@ pub fn run_command<A, E>(
|
|||
mode,
|
||||
));
|
||||
}
|
||||
window::Action::ToggleMaximize => {
|
||||
window.set_maximized(!window.is_maximized())
|
||||
}
|
||||
window::Action::FetchMode(tag) => {
|
||||
let mode = if window.is_visible().unwrap_or(true) {
|
||||
conversion::mode(window.fullscreen())
|
||||
|
|
|
|||
|
|
@ -4,6 +4,11 @@ use iced_native::window;
|
|||
|
||||
pub use window::{Event, Mode};
|
||||
|
||||
/// Begins dragging the window while the left mouse button is held.
|
||||
pub fn drag<Message>() -> Command<Message> {
|
||||
Command::single(command::Action::Window(window::Action::Drag))
|
||||
}
|
||||
|
||||
/// Resizes the window to the given logical dimensions.
|
||||
pub fn resize<Message>(width: u32, height: u32) -> Command<Message> {
|
||||
Command::single(command::Action::Window(window::Action::Resize {
|
||||
|
|
@ -12,6 +17,16 @@ pub fn resize<Message>(width: u32, height: u32) -> Command<Message> {
|
|||
}))
|
||||
}
|
||||
|
||||
/// Sets the window to maximized or back.
|
||||
pub fn maximize<Message>(value: bool) -> Command<Message> {
|
||||
Command::single(command::Action::Window(window::Action::Maximize(value)))
|
||||
}
|
||||
|
||||
/// Set the window to minimized or back.
|
||||
pub fn minimize<Message>(value: bool) -> Command<Message> {
|
||||
Command::single(command::Action::Window(window::Action::Minimize(value)))
|
||||
}
|
||||
|
||||
/// Moves a window to the given logical coordinates.
|
||||
pub fn move_to<Message>(x: i32, y: i32) -> Command<Message> {
|
||||
Command::single(command::Action::Window(window::Action::Move { x, y }))
|
||||
|
|
@ -22,6 +37,11 @@ pub fn set_mode<Message>(mode: Mode) -> Command<Message> {
|
|||
Command::single(command::Action::Window(window::Action::SetMode(mode)))
|
||||
}
|
||||
|
||||
/// Sets the window to maximized or back.
|
||||
pub fn toggle_maximize<Message>() -> Command<Message> {
|
||||
Command::single(command::Action::Window(window::Action::ToggleMaximize))
|
||||
}
|
||||
|
||||
/// Fetches the current [`Mode`] of the window.
|
||||
pub fn fetch_mode<Message>(
|
||||
f: impl FnOnce(Mode) -> Message + 'static,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue