summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2022-11-03 05:09:07 +0100
committerLibravatar Héctor Ramón Jiménez <hector0193@gmail.com>2022-11-03 05:09:07 +0100
commit921c94162e50b09604fafeeb319c4a424d64be0e (patch)
treeeff2e63d6cbef2732bd9f29c5733181191ef0a12
parent93e309f491a8941bafb919e75d660e65071475f4 (diff)
parent231d2fd8454eb9d24ba970131d4d7339cc0c8d51 (diff)
downloadiced-921c94162e50b09604fafeeb319c4a424d64be0e.tar.gz
iced-921c94162e50b09604fafeeb319c4a424d64be0e.tar.bz2
iced-921c94162e50b09604fafeeb319c4a424d64be0e.zip
Merge branch 'master' into fear/linear-gradients
-rw-r--r--Cargo.toml1
-rw-r--r--examples/cached/Cargo.toml10
-rw-r--r--examples/cached/src/main.rs139
-rw-r--r--examples/integration_wgpu/src/main.rs2
-rw-r--r--examples/lazy/Cargo.toml10
-rw-r--r--examples/lazy/src/main.rs139
-rw-r--r--examples/todos/Cargo.toml2
-rw-r--r--examples/todos/src/main.rs6
-rw-r--r--examples/websocket/Cargo.toml2
-rw-r--r--examples/websocket/src/main.rs5
-rw-r--r--glutin/Cargo.toml1
-rw-r--r--graphics/Cargo.toml2
-rw-r--r--graphics/src/window/compositor.rs6
-rw-r--r--lazy/src/lazy.rs362
-rw-r--r--lazy/src/lib.rs15
-rw-r--r--native/src/widget/pane_grid.rs46
-rw-r--r--native/src/widget/pane_grid/content.rs17
-rw-r--r--native/src/widget/pick_list.rs6
-rw-r--r--native/src/widget/text_input.rs8
-rw-r--r--native/src/window/action.rs20
-rw-r--r--style/Cargo.toml4
-rw-r--r--style/src/theme/palette.rs11
-rw-r--r--wgpu/Cargo.toml6
-rw-r--r--wgpu/src/window/compositor.rs9
-rw-r--r--winit/src/application.rs12
-rw-r--r--winit/src/window.rs20
26 files changed, 799 insertions, 62 deletions
diff --git a/Cargo.toml b/Cargo.toml
index e72d9152..f7d578ba 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -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",
diff --git a/examples/cached/Cargo.toml b/examples/cached/Cargo.toml
new file mode 100644
index 00000000..2c7edde2
--- /dev/null
+++ b/examples/cached/Cargo.toml
@@ -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" }
diff --git a/examples/cached/src/main.rs b/examples/cached/src/main.rs
new file mode 100644
index 00000000..8845b874
--- /dev/null
+++ b/examples/cached/src/main.rs
@@ -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",
+ }
+ )
+ }
+}
diff --git a/examples/integration_wgpu/src/main.rs b/examples/integration_wgpu/src/main.rs
index 69d46c3e..70f9a48b 100644
--- a/examples/integration_wgpu/src/main.rs
+++ b/examples/integration_wgpu/src/main.rs
@@ -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
},
);
diff --git a/examples/lazy/Cargo.toml b/examples/lazy/Cargo.toml
new file mode 100644
index 00000000..79255c25
--- /dev/null
+++ b/examples/lazy/Cargo.toml
@@ -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" }
diff --git a/examples/lazy/src/main.rs b/examples/lazy/src/main.rs
new file mode 100644
index 00000000..8845b874
--- /dev/null
+++ b/examples/lazy/src/main.rs
@@ -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",
+ }
+ )
+ }
+}
diff --git a/examples/todos/Cargo.toml b/examples/todos/Cargo.toml
index 2326ffc6..7ad4d558 100644
--- a/examples/todos/Cargo.toml
+++ b/examples/todos/Cargo.toml
@@ -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"
diff --git a/examples/todos/src/main.rs b/examples/todos/src/main.rs
index bddc0e71..be48ae8c 100644
--- a/examples/todos/src/main.rs
+++ b/examples/todos/src/main.rs
@@ -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 {
diff --git a/examples/websocket/Cargo.toml b/examples/websocket/Cargo.toml
index 3981f699..c25f067b 100644
--- a/examples/websocket/Cargo.toml
+++ b/examples/websocket/Cargo.toml
@@ -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"
diff --git a/examples/websocket/src/main.rs b/examples/websocket/src/main.rs
index 3902e04c..ff2929da 100644
--- a/examples/websocket/src/main.rs
+++ b/examples/websocket/src/main.rs
@@ -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);
diff --git a/glutin/Cargo.toml b/glutin/Cargo.toml
index d84f9d70..65b16947 100644
--- a/glutin/Cargo.toml
+++ b/glutin/Cargo.toml
@@ -29,6 +29,7 @@ path = "../native"
[dependencies.iced_winit]
version = "0.4"
path = "../winit"
+features = ["application"]
[dependencies.iced_graphics]
version = "0.3"
diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml
index ff6fcd4a..3b0e5236 100644
--- a/graphics/Cargo.toml
+++ b/graphics/Cargo.toml
@@ -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]
diff --git a/graphics/src/window/compositor.rs b/graphics/src/window/compositor.rs
index 0c4cadcd..52255666 100644
--- a/graphics/src/window/compositor.rs
+++ b/graphics/src/window/compositor.rs
@@ -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;
diff --git a/lazy/src/lazy.rs b/lazy/src/lazy.rs
new file mode 100644
index 00000000..d61cc77e
--- /dev/null
+++ b/lazy/src/lazy.rs
@@ -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)
+ }
+}
diff --git a/lazy/src/lib.rs b/lazy/src/lib.rs
index 3827746c..f49fe4b6 100644
--- a/lazy/src/lib.rs
+++ b/lazy/src/lib.rs
@@ -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.
diff --git a/native/src/widget/pane_grid.rs b/native/src/widget/pane_grid.rs
index d84fb7a0..96cf78ef 100644
--- a/native/src/widget/pane_grid.rs
+++ b/native/src/widget/pane_grid.rs
@@ -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)
diff --git a/native/src/widget/pane_grid/content.rs b/native/src/widget/pane_grid/content.rs
index 98ce2c4b..c236d820 100644
--- a/native/src/widget/pane_grid/content.rs
+++ b/native/src/widget/pane_grid/content.rs
@@ -115,25 +115,25 @@ where
let show_controls = bounds.contains(cursor_position);
- title_bar.draw(
- &tree.children[1],
+ self.body.as_widget().draw(
+ &tree.children[0],
renderer,
theme,
style,
- title_bar_layout,
+ body_layout,
cursor_position,
viewport,
- show_controls,
);
- self.body.as_widget().draw(
- &tree.children[0],
+ title_bar.draw(
+ &tree.children[1],
renderer,
theme,
style,
- body_layout,
+ title_bar_layout,
cursor_position,
viewport,
+ show_controls,
);
} else {
self.body.as_widget().draw(
@@ -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;
}
diff --git a/native/src/widget/pick_list.rs b/native/src/widget/pick_list.rs
index c334804e..896f5b35 100644
--- a/native/src/widget/pick_list.rs
+++ b/native/src/widget/pick_list.rs
@@ -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) {
diff --git a/native/src/widget/text_input.rs b/native/src/widget/text_input.rs
index c2d25520..54a6aaf8 100644
--- a/native/src/widget/text_input.rs
+++ b/native/src/widget/text_input.rs
@@ -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;
}
_ => {}
}
diff --git a/native/src/window/action.rs b/native/src/window/action.rs
index 73338e22..009dcc27 100644
--- a/native/src/window/action.rs
+++ b/native/src/window/action.rs
@@ -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"),
}
}
diff --git a/style/Cargo.toml b/style/Cargo.toml
index cf9d328b..4a482a4f 100644
--- a/style/Cargo.toml
+++ b/style/Cargo.toml
@@ -18,5 +18,5 @@ features = ["palette"]
[dependencies.palette]
version = "0.6"
-[dependencies.lazy_static]
-version = "1.4"
+[dependencies.once_cell]
+version = "1.15"
diff --git a/style/src/theme/palette.rs b/style/src/theme/palette.rs
index 81aa9cc7..4fb5e4c8 100644
--- a/style/src/theme/palette.rs
+++ b/style/src/theme/palette.rs
@@ -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 {
diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml
index 7174f80c..9a57e58b 100644
--- a/wgpu/Cargo.toml
+++ b/wgpu/Cargo.toml
@@ -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"
diff --git a/wgpu/src/window/compositor.rs b/wgpu/src/window/compositor.rs
index a36d2a87..6d0c36f6 100644
--- a/wgpu/src/window/compositor.rs
+++ b/wgpu/src/window/compositor.rs
@@ -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,
},
);
}
diff --git a/winit/src/application.rs b/winit/src/application.rs
index 0496aea9..939a50c9 100644
--- a/winit/src/application.rs
+++ b/winit/src/application.rs
@@ -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())
diff --git a/winit/src/window.rs b/winit/src/window.rs
index 265139f7..1e704c5b 100644
--- a/winit/src/window.rs
+++ b/winit/src/window.rs
@@ -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,