Merge branch 'master' into non-uniform-border-radius-for-quads
This commit is contained in:
commit
4029a1cdaa
147 changed files with 4828 additions and 2184 deletions
|
|
@ -316,6 +316,22 @@ where
|
|||
) {
|
||||
self.operation.focusable(state, id);
|
||||
}
|
||||
|
||||
fn scrollable(
|
||||
&mut self,
|
||||
state: &mut dyn widget::operation::Scrollable,
|
||||
id: Option<&widget::Id>,
|
||||
) {
|
||||
self.operation.scrollable(state, id);
|
||||
}
|
||||
|
||||
fn text_input(
|
||||
&mut self,
|
||||
state: &mut dyn widget::operation::TextInput,
|
||||
id: Option<&widget::Id>,
|
||||
) {
|
||||
self.operation.text_input(state, id);
|
||||
}
|
||||
}
|
||||
|
||||
self.widget
|
||||
|
|
@ -389,7 +405,7 @@ where
|
|||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b self,
|
||||
&'b mut self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
|
|
@ -544,7 +560,7 @@ where
|
|||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b self,
|
||||
&'b mut self,
|
||||
state: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//! Load and draw raster graphics.
|
||||
use crate::{Hasher, Rectangle};
|
||||
use crate::{Hasher, Rectangle, Size};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::hash::{Hash, Hasher as _};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
|
@ -21,15 +22,19 @@ impl Handle {
|
|||
}
|
||||
|
||||
/// Creates an image [`Handle`] containing the image pixels directly. This
|
||||
/// function expects the input data to be provided as a `Vec<u8>` of BGRA
|
||||
/// function expects the input data to be provided as a `Vec<u8>` of RGBA
|
||||
/// pixels.
|
||||
///
|
||||
/// This is useful if you have already decoded your image.
|
||||
pub fn from_pixels(width: u32, height: u32, pixels: Vec<u8>) -> Handle {
|
||||
Self::from_data(Data::Pixels {
|
||||
pub fn from_pixels(
|
||||
width: u32,
|
||||
height: u32,
|
||||
pixels: impl Into<Cow<'static, [u8]>>,
|
||||
) -> Handle {
|
||||
Self::from_data(Data::Rgba {
|
||||
width,
|
||||
height,
|
||||
pixels,
|
||||
pixels: pixels.into(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -39,8 +44,8 @@ impl Handle {
|
|||
///
|
||||
/// This is useful if you already have your image loaded in-memory, maybe
|
||||
/// because you downloaded or generated it procedurally.
|
||||
pub fn from_memory(bytes: Vec<u8>) -> Handle {
|
||||
Self::from_data(Data::Bytes(bytes))
|
||||
pub fn from_memory(bytes: impl Into<Cow<'static, [u8]>>) -> Handle {
|
||||
Self::from_data(Data::Bytes(bytes.into()))
|
||||
}
|
||||
|
||||
fn from_data(data: Data) -> Handle {
|
||||
|
|
@ -86,16 +91,16 @@ pub enum Data {
|
|||
Path(PathBuf),
|
||||
|
||||
/// In-memory data
|
||||
Bytes(Vec<u8>),
|
||||
Bytes(Cow<'static, [u8]>),
|
||||
|
||||
/// Decoded image pixels in BGRA format.
|
||||
Pixels {
|
||||
/// Decoded image pixels in RGBA format.
|
||||
Rgba {
|
||||
/// The width of the image.
|
||||
width: u32,
|
||||
/// The height of the image.
|
||||
height: u32,
|
||||
/// The pixels.
|
||||
pixels: Vec<u8>,
|
||||
pixels: Cow<'static, [u8]>,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +109,7 @@ impl std::fmt::Debug for Data {
|
|||
match self {
|
||||
Data::Path(path) => write!(f, "Path({:?})", path),
|
||||
Data::Bytes(_) => write!(f, "Bytes(...)"),
|
||||
Data::Pixels { width, height, .. } => {
|
||||
Data::Rgba { width, height, .. } => {
|
||||
write!(f, "Pixels({} * {})", width, height)
|
||||
}
|
||||
}
|
||||
|
|
@ -121,7 +126,7 @@ pub trait Renderer: crate::Renderer {
|
|||
type Handle: Clone + Hash;
|
||||
|
||||
/// Returns the dimensions of an image for the given [`Handle`].
|
||||
fn dimensions(&self, handle: &Self::Handle) -> (u32, u32);
|
||||
fn dimensions(&self, handle: &Self::Handle) -> Size<u32>;
|
||||
|
||||
/// Draws an image with the given [`Handle`] and inside the provided
|
||||
/// `bounds`.
|
||||
|
|
|
|||
|
|
@ -23,8 +23,8 @@
|
|||
//! - Build a new renderer, see the [renderer] module.
|
||||
//! - Build a custom widget, start at the [`Widget`] trait.
|
||||
//!
|
||||
//! [`iced_core`]: https://github.com/iced-rs/iced/tree/0.4/core
|
||||
//! [`iced_winit`]: https://github.com/iced-rs/iced/tree/0.4/winit
|
||||
//! [`iced_core`]: https://github.com/iced-rs/iced/tree/0.5/core
|
||||
//! [`iced_winit`]: https://github.com/iced-rs/iced/tree/0.5/winit
|
||||
//! [`druid`]: https://github.com/xi-editor/druid
|
||||
//! [`raw-window-handle`]: https://github.com/rust-windowing/raw-window-handle
|
||||
//! [renderer]: crate::renderer
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ use crate::layout;
|
|||
use crate::mouse;
|
||||
use crate::renderer;
|
||||
use crate::widget;
|
||||
use crate::widget::tree::{self, Tree};
|
||||
use crate::widget::Tree;
|
||||
use crate::{Clipboard, Layout, Point, Rectangle, Shell, Size};
|
||||
|
||||
/// An interactive component that can be displayed on top of other widgets.
|
||||
|
|
@ -42,31 +42,9 @@ where
|
|||
cursor_position: Point,
|
||||
);
|
||||
|
||||
/// Returns the [`Tag`] of the [`Widget`].
|
||||
///
|
||||
/// [`Tag`]: tree::Tag
|
||||
fn tag(&self) -> tree::Tag {
|
||||
tree::Tag::stateless()
|
||||
}
|
||||
|
||||
/// Returns the [`State`] of the [`Widget`].
|
||||
///
|
||||
/// [`State`]: tree::State
|
||||
fn state(&self) -> tree::State {
|
||||
tree::State::None
|
||||
}
|
||||
|
||||
/// Returns the state [`Tree`] of the children of the [`Widget`].
|
||||
fn children(&self) -> Vec<Tree> {
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
/// Reconciliates the [`Widget`] with the provided [`Tree`].
|
||||
fn diff(&self, _tree: &mut Tree) {}
|
||||
|
||||
/// Applies an [`Operation`] to the [`Widget`].
|
||||
/// Applies a [`widget::Operation`] to the [`Overlay`].
|
||||
fn operate(
|
||||
&self,
|
||||
&mut self,
|
||||
_layout: Layout<'_>,
|
||||
_operation: &mut dyn widget::Operation<Message>,
|
||||
) {
|
||||
|
|
@ -115,7 +93,7 @@ where
|
|||
/// This method will generally only be used by advanced users that are
|
||||
/// implementing the [`Widget`](crate::Widget) trait.
|
||||
pub fn from_children<'a, Message, Renderer>(
|
||||
children: &'a [crate::Element<'_, Message, Renderer>],
|
||||
children: &'a mut [crate::Element<'_, Message, Renderer>],
|
||||
tree: &'a mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
|
|
@ -124,11 +102,11 @@ where
|
|||
Renderer: crate::Renderer,
|
||||
{
|
||||
children
|
||||
.iter()
|
||||
.iter_mut()
|
||||
.zip(&mut tree.children)
|
||||
.zip(layout.children())
|
||||
.filter_map(|((child, state), layout)| {
|
||||
child.as_widget().overlay(state, layout, renderer)
|
||||
child.as_widget_mut().overlay(state, layout, renderer)
|
||||
})
|
||||
.next()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -104,9 +104,9 @@ where
|
|||
.draw(renderer, theme, style, layout, cursor_position)
|
||||
}
|
||||
|
||||
/// Applies an [`Operation`] to the [`Element`].
|
||||
/// Applies a [`widget::Operation`] to the [`Element`].
|
||||
pub fn operate(
|
||||
&self,
|
||||
&mut self,
|
||||
layout: Layout<'_>,
|
||||
operation: &mut dyn widget::Operation<Message>,
|
||||
) {
|
||||
|
|
@ -141,6 +141,57 @@ where
|
|||
self.content.layout(renderer, bounds, position)
|
||||
}
|
||||
|
||||
fn operate(
|
||||
&mut self,
|
||||
layout: Layout<'_>,
|
||||
operation: &mut dyn widget::Operation<B>,
|
||||
) {
|
||||
struct MapOperation<'a, B> {
|
||||
operation: &'a mut dyn widget::Operation<B>,
|
||||
}
|
||||
|
||||
impl<'a, T, B> widget::Operation<T> for MapOperation<'a, B> {
|
||||
fn container(
|
||||
&mut self,
|
||||
id: Option<&widget::Id>,
|
||||
operate_on_children: &mut dyn FnMut(
|
||||
&mut dyn widget::Operation<T>,
|
||||
),
|
||||
) {
|
||||
self.operation.container(id, &mut |operation| {
|
||||
operate_on_children(&mut MapOperation { operation });
|
||||
});
|
||||
}
|
||||
|
||||
fn focusable(
|
||||
&mut self,
|
||||
state: &mut dyn widget::operation::Focusable,
|
||||
id: Option<&widget::Id>,
|
||||
) {
|
||||
self.operation.focusable(state, id);
|
||||
}
|
||||
|
||||
fn scrollable(
|
||||
&mut self,
|
||||
state: &mut dyn widget::operation::Scrollable,
|
||||
id: Option<&widget::Id>,
|
||||
) {
|
||||
self.operation.scrollable(state, id);
|
||||
}
|
||||
|
||||
fn text_input(
|
||||
&mut self,
|
||||
state: &mut dyn widget::operation::TextInput,
|
||||
id: Option<&widget::Id>,
|
||||
) {
|
||||
self.operation.text_input(state, id)
|
||||
}
|
||||
}
|
||||
|
||||
self.content
|
||||
.operate(layout, &mut MapOperation { operation });
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
event: Event,
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use crate::text::{self, Text};
|
|||
use crate::touch;
|
||||
use crate::widget::container::{self, Container};
|
||||
use crate::widget::scrollable::{self, Scrollable};
|
||||
use crate::widget::tree::{self, Tree};
|
||||
use crate::widget::Tree;
|
||||
use crate::{
|
||||
Clipboard, Color, Element, Layout, Length, Padding, Point, Rectangle,
|
||||
Shell, Size, Vector, Widget,
|
||||
|
|
@ -178,7 +178,7 @@ where
|
|||
font,
|
||||
text_size,
|
||||
padding,
|
||||
style,
|
||||
style: style.clone(),
|
||||
}));
|
||||
|
||||
state.tree.diff(&container as &dyn Widget<_, _>);
|
||||
|
|
@ -199,18 +199,6 @@ where
|
|||
Renderer: text::Renderer,
|
||||
Renderer::Theme: StyleSheet + container::StyleSheet,
|
||||
{
|
||||
fn tag(&self) -> tree::Tag {
|
||||
self.container.tag()
|
||||
}
|
||||
|
||||
fn state(&self) -> tree::State {
|
||||
self.container.state()
|
||||
}
|
||||
|
||||
fn children(&self) -> Vec<Tree> {
|
||||
self.container.children()
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&self,
|
||||
renderer: &Renderer,
|
||||
|
|
@ -288,7 +276,7 @@ where
|
|||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
) {
|
||||
let appearance = theme.appearance(self.style);
|
||||
let appearance = theme.appearance(&self.style);
|
||||
let bounds = layout.bounds();
|
||||
|
||||
renderer.fill_quad(
|
||||
|
|
@ -460,7 +448,7 @@ where
|
|||
_cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
) {
|
||||
let appearance = theme.appearance(self.style);
|
||||
let appearance = theme.appearance(&self.style);
|
||||
let bounds = layout.bounds();
|
||||
|
||||
let text_size =
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ where
|
|||
/// Check out the [`websocket`] example, which showcases this pattern to maintain a WebSocket
|
||||
/// connection open.
|
||||
///
|
||||
/// [`websocket`]: https://github.com/iced-rs/iced/tree/0.4/examples/websocket
|
||||
/// [`websocket`]: https://github.com/iced-rs/iced/tree/0.5/examples/websocket
|
||||
pub fn unfold<I, T, Fut, Message>(
|
||||
id: I,
|
||||
initial: T,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//! Load and draw vector graphics.
|
||||
use crate::{Hasher, Rectangle};
|
||||
use crate::{Hasher, Rectangle, Size};
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::hash::{Hash, Hasher as _};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
|
|
@ -24,7 +25,7 @@ impl Handle {
|
|||
///
|
||||
/// This is useful if you already have your SVG data in-memory, maybe
|
||||
/// because you downloaded or generated it procedurally.
|
||||
pub fn from_memory(bytes: impl Into<Vec<u8>>) -> Handle {
|
||||
pub fn from_memory(bytes: impl Into<Cow<'static, [u8]>>) -> Handle {
|
||||
Self::from_data(Data::Bytes(bytes.into()))
|
||||
}
|
||||
|
||||
|
|
@ -64,7 +65,7 @@ pub enum Data {
|
|||
/// In-memory data
|
||||
///
|
||||
/// Can contain an SVG string or a gzip compressed data.
|
||||
Bytes(Vec<u8>),
|
||||
Bytes(Cow<'static, [u8]>),
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Data {
|
||||
|
|
@ -81,7 +82,7 @@ impl std::fmt::Debug for Data {
|
|||
/// [renderer]: crate::renderer
|
||||
pub trait Renderer: crate::Renderer {
|
||||
/// Returns the default dimensions of an SVG for the given [`Handle`].
|
||||
fn dimensions(&self, handle: &Handle) -> (u32, u32);
|
||||
fn dimensions(&self, handle: &Handle) -> Size<u32>;
|
||||
|
||||
/// Draws an SVG with the given [`Handle`] and inside the provided `bounds`.
|
||||
fn draw(&mut self, handle: Handle, bounds: Rectangle);
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ use crate::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size};
|
|||
/// The [`integration_opengl`] & [`integration_wgpu`] examples use a
|
||||
/// [`UserInterface`] to integrate Iced in an existing graphical application.
|
||||
///
|
||||
/// [`integration_opengl`]: https://github.com/iced-rs/iced/tree/0.4/examples/integration_opengl
|
||||
/// [`integration_wgpu`]: https://github.com/iced-rs/iced/tree/0.4/examples/integration_wgpu
|
||||
/// [`integration_opengl`]: https://github.com/iced-rs/iced/tree/0.5/examples/integration_opengl
|
||||
/// [`integration_wgpu`]: https://github.com/iced-rs/iced/tree/0.5/examples/integration_wgpu
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct UserInterface<'a, Message, Renderer> {
|
||||
root: Element<'a, Message, Renderer>,
|
||||
|
|
@ -190,7 +190,7 @@ where
|
|||
|
||||
let mut state = State::Updated;
|
||||
let mut manual_overlay =
|
||||
ManuallyDrop::new(self.root.as_widget().overlay(
|
||||
ManuallyDrop::new(self.root.as_widget_mut().overlay(
|
||||
&mut self.state,
|
||||
Layout::new(&self.base),
|
||||
renderer,
|
||||
|
|
@ -226,7 +226,7 @@ where
|
|||
);
|
||||
|
||||
manual_overlay =
|
||||
ManuallyDrop::new(self.root.as_widget().overlay(
|
||||
ManuallyDrop::new(self.root.as_widget_mut().overlay(
|
||||
&mut self.state,
|
||||
Layout::new(&self.base),
|
||||
renderer,
|
||||
|
|
@ -285,6 +285,10 @@ where
|
|||
&mut shell,
|
||||
);
|
||||
|
||||
if matches!(event_status, event::Status::Captured) {
|
||||
self.overlay = None;
|
||||
}
|
||||
|
||||
shell.revalidate_layout(|| {
|
||||
self.base = renderer.layout(
|
||||
&self.root,
|
||||
|
|
@ -391,11 +395,11 @@ where
|
|||
|
||||
let viewport = Rectangle::with_size(self.bounds);
|
||||
|
||||
let base_cursor = if let Some(overlay) = self.root.as_widget().overlay(
|
||||
&mut self.state,
|
||||
Layout::new(&self.base),
|
||||
renderer,
|
||||
) {
|
||||
let base_cursor = if let Some(overlay) = self
|
||||
.root
|
||||
.as_widget_mut()
|
||||
.overlay(&mut self.state, Layout::new(&self.base), renderer)
|
||||
{
|
||||
let overlay_layout = self
|
||||
.overlay
|
||||
.take()
|
||||
|
|
@ -448,7 +452,7 @@ where
|
|||
overlay
|
||||
.as_ref()
|
||||
.and_then(|layout| {
|
||||
root.as_widget()
|
||||
root.as_widget_mut()
|
||||
.overlay(&mut self.state, Layout::new(base), renderer)
|
||||
.map(|overlay| {
|
||||
let overlay_interaction = overlay.mouse_interaction(
|
||||
|
|
@ -492,14 +496,19 @@ where
|
|||
operation,
|
||||
);
|
||||
|
||||
if let Some(layout) = self.overlay.as_ref() {
|
||||
if let Some(overlay) = self.root.as_widget().overlay(
|
||||
&mut self.state,
|
||||
Layout::new(&self.base),
|
||||
renderer,
|
||||
) {
|
||||
overlay.operate(Layout::new(layout), operation);
|
||||
if let Some(mut overlay) = self.root.as_widget_mut().overlay(
|
||||
&mut self.state,
|
||||
Layout::new(&self.base),
|
||||
renderer,
|
||||
) {
|
||||
if self.overlay.is_none() {
|
||||
self.overlay = Some(overlay.layout(renderer, self.bounds));
|
||||
}
|
||||
|
||||
overlay.operate(
|
||||
Layout::new(self.overlay.as_ref().unwrap()),
|
||||
operation,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -107,12 +107,12 @@ use crate::{Clipboard, Layout, Length, Point, Rectangle, Shell};
|
|||
/// - [`geometry`], a custom widget showcasing how to draw geometry with the
|
||||
/// `Mesh2D` primitive in [`iced_wgpu`].
|
||||
///
|
||||
/// [examples]: https://github.com/iced-rs/iced/tree/0.4/examples
|
||||
/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.4/examples/bezier_tool
|
||||
/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.4/examples/custom_widget
|
||||
/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.4/examples/geometry
|
||||
/// [examples]: https://github.com/iced-rs/iced/tree/0.5/examples
|
||||
/// [`bezier_tool`]: https://github.com/iced-rs/iced/tree/0.5/examples/bezier_tool
|
||||
/// [`custom_widget`]: https://github.com/iced-rs/iced/tree/0.5/examples/custom_widget
|
||||
/// [`geometry`]: https://github.com/iced-rs/iced/tree/0.5/examples/geometry
|
||||
/// [`lyon`]: https://github.com/nical/lyon
|
||||
/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.4/wgpu
|
||||
/// [`iced_wgpu`]: https://github.com/iced-rs/iced/tree/0.5/wgpu
|
||||
pub trait Widget<Message, Renderer>
|
||||
where
|
||||
Renderer: crate::Renderer,
|
||||
|
|
@ -208,7 +208,7 @@ where
|
|||
|
||||
/// Returns the overlay of the [`Widget`], if there is any.
|
||||
fn overlay<'a>(
|
||||
&'a self,
|
||||
&'a mut self,
|
||||
_state: &'a mut Tree,
|
||||
_layout: Layout<'_>,
|
||||
_renderer: &Renderer,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
use crate::widget::operation::{self, Operation};
|
||||
use crate::widget::operation::{self, Focusable, Operation, Scrollable};
|
||||
use crate::widget::Id;
|
||||
|
||||
use iced_futures::MaybeSend;
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
/// An operation to be performed on the widget tree.
|
||||
#[allow(missing_debug_implementations)]
|
||||
pub struct Action<T>(Box<dyn Operation<T>>);
|
||||
|
|
@ -24,7 +26,7 @@ impl<T> Action<T> {
|
|||
{
|
||||
Action(Box::new(Map {
|
||||
operation: self.0,
|
||||
f: Box::new(f),
|
||||
f: Rc::new(f),
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
@ -37,7 +39,7 @@ impl<T> Action<T> {
|
|||
#[allow(missing_debug_implementations)]
|
||||
struct Map<A, B> {
|
||||
operation: Box<dyn Operation<A>>,
|
||||
f: Box<dyn Fn(A) -> B>,
|
||||
f: Rc<dyn Fn(A) -> B>,
|
||||
}
|
||||
|
||||
impl<A, B> Operation<B> for Map<A, B>
|
||||
|
|
@ -50,30 +52,44 @@ where
|
|||
id: Option<&Id>,
|
||||
operate_on_children: &mut dyn FnMut(&mut dyn Operation<B>),
|
||||
) {
|
||||
struct MapRef<'a, A, B> {
|
||||
struct MapRef<'a, A> {
|
||||
operation: &'a mut dyn Operation<A>,
|
||||
f: &'a dyn Fn(A) -> B,
|
||||
}
|
||||
|
||||
impl<'a, A, B> Operation<B> for MapRef<'a, A, B> {
|
||||
impl<'a, A, B> Operation<B> for MapRef<'a, A> {
|
||||
fn container(
|
||||
&mut self,
|
||||
id: Option<&Id>,
|
||||
operate_on_children: &mut dyn FnMut(&mut dyn Operation<B>),
|
||||
) {
|
||||
let Self { operation, f } = self;
|
||||
let Self { operation, .. } = self;
|
||||
|
||||
operation.container(id, &mut |operation| {
|
||||
operate_on_children(&mut MapRef { operation, f });
|
||||
operate_on_children(&mut MapRef { operation });
|
||||
});
|
||||
}
|
||||
|
||||
fn scrollable(
|
||||
&mut self,
|
||||
state: &mut dyn Scrollable,
|
||||
id: Option<&Id>,
|
||||
) {
|
||||
self.operation.scrollable(state, id);
|
||||
}
|
||||
|
||||
fn focusable(
|
||||
&mut self,
|
||||
state: &mut dyn Focusable,
|
||||
id: Option<&Id>,
|
||||
) {
|
||||
self.operation.focusable(state, id);
|
||||
}
|
||||
}
|
||||
|
||||
let Self { operation, f } = self;
|
||||
let Self { operation, .. } = self;
|
||||
|
||||
MapRef {
|
||||
operation: operation.as_mut(),
|
||||
f,
|
||||
}
|
||||
.container(id, operate_on_children);
|
||||
}
|
||||
|
|
@ -85,4 +101,35 @@ where
|
|||
) {
|
||||
self.operation.focusable(state, id);
|
||||
}
|
||||
|
||||
fn scrollable(
|
||||
&mut self,
|
||||
state: &mut dyn operation::Scrollable,
|
||||
id: Option<&Id>,
|
||||
) {
|
||||
self.operation.scrollable(state, id);
|
||||
}
|
||||
|
||||
fn text_input(
|
||||
&mut self,
|
||||
state: &mut dyn operation::TextInput,
|
||||
id: Option<&Id>,
|
||||
) {
|
||||
self.operation.text_input(state, id);
|
||||
}
|
||||
|
||||
fn finish(&self) -> operation::Outcome<B> {
|
||||
match self.operation.finish() {
|
||||
operation::Outcome::None => operation::Outcome::None,
|
||||
operation::Outcome::Some(output) => {
|
||||
operation::Outcome::Some((self.f)(output))
|
||||
}
|
||||
operation::Outcome::Chain(next) => {
|
||||
operation::Outcome::Chain(Box::new(Map {
|
||||
operation: next,
|
||||
f: self.f.clone(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -231,7 +231,7 @@ where
|
|||
cursor_position,
|
||||
self.on_press.is_some(),
|
||||
theme,
|
||||
self.style,
|
||||
&self.style,
|
||||
|| tree.state.downcast_ref::<State>(),
|
||||
);
|
||||
|
||||
|
|
@ -260,12 +260,12 @@ where
|
|||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b self,
|
||||
&'b mut self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
||||
self.content.as_widget().overlay(
|
||||
self.content.as_widget_mut().overlay(
|
||||
&mut tree.children[0],
|
||||
layout.children().next().unwrap(),
|
||||
renderer,
|
||||
|
|
@ -361,7 +361,7 @@ pub fn draw<'a, Renderer: crate::Renderer>(
|
|||
style_sheet: &dyn StyleSheet<
|
||||
Style = <Renderer::Theme as StyleSheet>::Style,
|
||||
>,
|
||||
style: <Renderer::Theme as StyleSheet>::Style,
|
||||
style: &<Renderer::Theme as StyleSheet>::Style,
|
||||
state: impl FnOnce() -> &'a State,
|
||||
) -> Appearance
|
||||
where
|
||||
|
|
@ -426,13 +426,14 @@ pub fn layout<Renderer>(
|
|||
padding: Padding,
|
||||
layout_content: impl FnOnce(&Renderer, &layout::Limits) -> layout::Node,
|
||||
) -> layout::Node {
|
||||
let limits = limits.width(width).height(height).pad(padding);
|
||||
let limits = limits.width(width).height(height);
|
||||
|
||||
let mut content = layout_content(renderer, &limits.pad(padding));
|
||||
let padding = padding.fit(content.size(), limits.max());
|
||||
let size = limits.pad(padding).resolve(content.size()).pad(padding);
|
||||
|
||||
let mut content = layout_content(renderer, &limits);
|
||||
content.move_to(Point::new(padding.left.into(), padding.top.into()));
|
||||
|
||||
let size = limits.resolve(content.size()).pad(padding);
|
||||
|
||||
layout::Node::with_children(size, vec![content])
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -224,9 +224,9 @@ where
|
|||
let mut children = layout.children();
|
||||
|
||||
let custom_style = if is_mouse_over {
|
||||
theme.hovered(self.style, self.is_checked)
|
||||
theme.hovered(&self.style, self.is_checked)
|
||||
} else {
|
||||
theme.active(self.style, self.is_checked)
|
||||
theme.active(&self.style, self.is_checked)
|
||||
};
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -242,12 +242,12 @@ where
|
|||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b self,
|
||||
&'b mut self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
||||
overlay::from_children(&self.children, tree, layout, renderer)
|
||||
overlay::from_children(&mut self.children, tree, layout, renderer)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ where
|
|||
cursor_position: Point,
|
||||
viewport: &Rectangle,
|
||||
) {
|
||||
let style = theme.appearance(self.style);
|
||||
let style = theme.appearance(&self.style);
|
||||
|
||||
draw_background(renderer, &style, layout.bounds());
|
||||
|
||||
|
|
@ -248,12 +248,12 @@ where
|
|||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b self,
|
||||
&'b mut self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
||||
self.content.as_widget().overlay(
|
||||
self.content.as_widget_mut().overlay(
|
||||
&mut tree.children[0],
|
||||
layout.children().next().unwrap(),
|
||||
renderer,
|
||||
|
|
@ -293,11 +293,11 @@ pub fn layout<Renderer>(
|
|||
.max_width(max_width)
|
||||
.max_height(max_height)
|
||||
.width(width)
|
||||
.height(height)
|
||||
.pad(padding);
|
||||
.height(height);
|
||||
|
||||
let mut content = layout_content(renderer, &limits.loose());
|
||||
let size = limits.resolve(content.size());
|
||||
let mut content = layout_content(renderer, &limits.pad(padding).loose());
|
||||
let padding = padding.fit(content.size(), limits.max());
|
||||
let size = limits.pad(padding).resolve(content.size());
|
||||
|
||||
content.move_to(Point::new(padding.left.into(), padding.top.into()));
|
||||
content.align(
|
||||
|
|
@ -309,7 +309,7 @@ pub fn layout<Renderer>(
|
|||
layout::Node::with_children(size.pad(padding), vec![content])
|
||||
}
|
||||
|
||||
/// Draws the background of a [`Container`] given its [`Style`] and its `bounds`.
|
||||
/// Draws the background of a [`Container`] given its [`Appearance`] and its `bounds`.
|
||||
pub fn draw_background<Renderer>(
|
||||
renderer: &mut Renderer,
|
||||
appearance: &Appearance,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
//! Helper functions to create pure widgets.
|
||||
use crate::overlay;
|
||||
use crate::widget;
|
||||
use crate::{Element, Length};
|
||||
|
||||
|
|
@ -18,7 +19,7 @@ macro_rules! column {
|
|||
);
|
||||
}
|
||||
|
||||
/// Creates a [Row`] with the given children.
|
||||
/// Creates a [`Row`] with the given children.
|
||||
///
|
||||
/// [`Row`]: widget::Row
|
||||
#[macro_export]
|
||||
|
|
@ -84,6 +85,7 @@ pub fn button<'a, Message, Renderer>(
|
|||
where
|
||||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: widget::button::StyleSheet,
|
||||
<Renderer::Theme as widget::button::StyleSheet>::Style: Default,
|
||||
{
|
||||
widget::Button::new(content)
|
||||
}
|
||||
|
|
@ -208,7 +210,12 @@ where
|
|||
T: ToString + Eq + 'static,
|
||||
[T]: ToOwned<Owned = Vec<T>>,
|
||||
Renderer: crate::text::Renderer,
|
||||
Renderer::Theme: widget::pick_list::StyleSheet,
|
||||
Renderer::Theme: widget::pick_list::StyleSheet
|
||||
+ widget::scrollable::StyleSheet
|
||||
+ overlay::menu::StyleSheet
|
||||
+ widget::container::StyleSheet,
|
||||
<Renderer::Theme as overlay::menu::StyleSheet>::Style:
|
||||
From<<Renderer::Theme as widget::pick_list::StyleSheet>::Style>,
|
||||
{
|
||||
widget::PickList::new(options, selected, on_selected)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ where
|
|||
{
|
||||
// The raw w/h of the underlying image
|
||||
let image_size = {
|
||||
let (width, height) = renderer.dimensions(handle);
|
||||
let Size { width, height } = renderer.dimensions(handle);
|
||||
|
||||
Size::new(width as f32, height as f32)
|
||||
};
|
||||
|
|
@ -149,7 +149,7 @@ where
|
|||
_cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
) {
|
||||
let (width, height) = renderer.dimensions(&self.handle);
|
||||
let Size { width, height } = renderer.dimensions(&self.handle);
|
||||
let image_size = Size::new(width as f32, height as f32);
|
||||
|
||||
let bounds = layout.bounds();
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ where
|
|||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let (width, height) = renderer.dimensions(&self.handle);
|
||||
let Size { width, height } = renderer.dimensions(&self.handle);
|
||||
|
||||
let mut size = limits
|
||||
.width(self.width)
|
||||
|
|
@ -409,7 +409,7 @@ pub fn image_size<Renderer>(
|
|||
where
|
||||
Renderer: image::Renderer,
|
||||
{
|
||||
let (width, height) = renderer.dimensions(handle);
|
||||
let Size { width, height } = renderer.dimensions(handle);
|
||||
|
||||
let (width, height) = {
|
||||
let dimensions = (width as f32, height as f32);
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
//! Query or update internal widget state.
|
||||
pub mod focusable;
|
||||
pub mod scrollable;
|
||||
pub mod text_input;
|
||||
|
||||
pub use focusable::Focusable;
|
||||
pub use scrollable::Scrollable;
|
||||
pub use text_input::TextInput;
|
||||
|
||||
use crate::widget::Id;
|
||||
|
||||
|
|
@ -28,6 +30,9 @@ pub trait Operation<T> {
|
|||
/// Operates on a widget that can be scrolled.
|
||||
fn scrollable(&mut self, _state: &mut dyn Scrollable, _id: Option<&Id>) {}
|
||||
|
||||
/// Operates on a widget that has text input.
|
||||
fn text_input(&mut self, _state: &mut dyn TextInput, _id: Option<&Id>) {}
|
||||
|
||||
/// Finishes the [`Operation`] and returns its [`Outcome`].
|
||||
fn finish(&self) -> Outcome<T> {
|
||||
Outcome::None
|
||||
|
|
@ -58,3 +63,46 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Produces an [`Operation`] that applies the given [`Operation`] to the
|
||||
/// children of a container with the given [`Id`].
|
||||
pub fn scoped<T: 'static>(
|
||||
target: Id,
|
||||
operation: impl Operation<T> + 'static,
|
||||
) -> impl Operation<T> {
|
||||
struct ScopedOperation<Message> {
|
||||
target: Id,
|
||||
operation: Box<dyn Operation<Message>>,
|
||||
}
|
||||
|
||||
impl<Message: 'static> Operation<Message> for ScopedOperation<Message> {
|
||||
fn container(
|
||||
&mut self,
|
||||
id: Option<&Id>,
|
||||
operate_on_children: &mut dyn FnMut(&mut dyn Operation<Message>),
|
||||
) {
|
||||
if id == Some(&self.target) {
|
||||
operate_on_children(self.operation.as_mut());
|
||||
} else {
|
||||
operate_on_children(self);
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(&self) -> Outcome<Message> {
|
||||
match self.operation.finish() {
|
||||
Outcome::Chain(next) => {
|
||||
Outcome::Chain(Box::new(ScopedOperation {
|
||||
target: self.target.clone(),
|
||||
operation: next,
|
||||
}))
|
||||
}
|
||||
outcome => outcome,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ScopedOperation {
|
||||
target,
|
||||
operation: Box::new(operation),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,3 +167,37 @@ pub fn focus_next<T>() -> impl Operation<T> {
|
|||
|
||||
count(|count| FocusNext { count, current: 0 })
|
||||
}
|
||||
|
||||
/// Produces an [`Operation`] that searches for the current focused widget
|
||||
/// and stores its ID. This ignores widgets that do not have an ID.
|
||||
pub fn find_focused() -> impl Operation<Id> {
|
||||
struct FindFocused {
|
||||
focused: Option<Id>,
|
||||
}
|
||||
|
||||
impl Operation<Id> for FindFocused {
|
||||
fn focusable(&mut self, state: &mut dyn Focusable, id: Option<&Id>) {
|
||||
if state.is_focused() && id.is_some() {
|
||||
self.focused = id.cloned();
|
||||
}
|
||||
}
|
||||
|
||||
fn container(
|
||||
&mut self,
|
||||
_id: Option<&Id>,
|
||||
operate_on_children: &mut dyn FnMut(&mut dyn Operation<Id>),
|
||||
) {
|
||||
operate_on_children(self)
|
||||
}
|
||||
|
||||
fn finish(&self) -> Outcome<Id> {
|
||||
if let Some(id) = &self.focused {
|
||||
Outcome::Some(id.clone())
|
||||
} else {
|
||||
Outcome::None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FindFocused { focused: None }
|
||||
}
|
||||
|
|
|
|||
131
native/src/widget/operation/text_input.rs
Normal file
131
native/src/widget/operation/text_input.rs
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
//! Operate on widgets that have text input.
|
||||
use crate::widget::operation::Operation;
|
||||
use crate::widget::Id;
|
||||
|
||||
/// The internal state of a widget that has text input.
|
||||
pub trait TextInput {
|
||||
/// Moves the cursor of the text input to the front of the input text.
|
||||
fn move_cursor_to_front(&mut self);
|
||||
/// Moves the cursor of the text input to the end of the input text.
|
||||
fn move_cursor_to_end(&mut self);
|
||||
/// Moves the cursor of the text input to an arbitrary location.
|
||||
fn move_cursor_to(&mut self, position: usize);
|
||||
/// Selects all the content of the text input.
|
||||
fn select_all(&mut self);
|
||||
}
|
||||
|
||||
/// Produces an [`Operation`] that moves the cursor of the widget with the given [`Id`] to the
|
||||
/// front.
|
||||
pub fn move_cursor_to_front<T>(target: Id) -> impl Operation<T> {
|
||||
struct MoveCursor {
|
||||
target: Id,
|
||||
}
|
||||
|
||||
impl<T> Operation<T> for MoveCursor {
|
||||
fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
|
||||
match id {
|
||||
Some(id) if id == &self.target => {
|
||||
state.move_cursor_to_front();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn container(
|
||||
&mut self,
|
||||
_id: Option<&Id>,
|
||||
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
|
||||
) {
|
||||
operate_on_children(self)
|
||||
}
|
||||
}
|
||||
|
||||
MoveCursor { target }
|
||||
}
|
||||
|
||||
/// Produces an [`Operation`] that moves the cursor of the widget with the given [`Id`] to the
|
||||
/// end.
|
||||
pub fn move_cursor_to_end<T>(target: Id) -> impl Operation<T> {
|
||||
struct MoveCursor {
|
||||
target: Id,
|
||||
}
|
||||
|
||||
impl<T> Operation<T> for MoveCursor {
|
||||
fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
|
||||
match id {
|
||||
Some(id) if id == &self.target => {
|
||||
state.move_cursor_to_end();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn container(
|
||||
&mut self,
|
||||
_id: Option<&Id>,
|
||||
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
|
||||
) {
|
||||
operate_on_children(self)
|
||||
}
|
||||
}
|
||||
|
||||
MoveCursor { target }
|
||||
}
|
||||
|
||||
/// Produces an [`Operation`] that moves the cursor of the widget with the given [`Id`] to the
|
||||
/// provided position.
|
||||
pub fn move_cursor_to<T>(target: Id, position: usize) -> impl Operation<T> {
|
||||
struct MoveCursor {
|
||||
target: Id,
|
||||
position: usize,
|
||||
}
|
||||
|
||||
impl<T> Operation<T> for MoveCursor {
|
||||
fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
|
||||
match id {
|
||||
Some(id) if id == &self.target => {
|
||||
state.move_cursor_to(self.position);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn container(
|
||||
&mut self,
|
||||
_id: Option<&Id>,
|
||||
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
|
||||
) {
|
||||
operate_on_children(self)
|
||||
}
|
||||
}
|
||||
|
||||
MoveCursor { target, position }
|
||||
}
|
||||
|
||||
/// Produces an [`Operation`] that selects all the content of the widget with the given [`Id`].
|
||||
pub fn select_all<T>(target: Id) -> impl Operation<T> {
|
||||
struct MoveCursor {
|
||||
target: Id,
|
||||
}
|
||||
|
||||
impl<T> Operation<T> for MoveCursor {
|
||||
fn text_input(&mut self, state: &mut dyn TextInput, id: Option<&Id>) {
|
||||
match id {
|
||||
Some(id) if id == &self.target => {
|
||||
state.select_all();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn container(
|
||||
&mut self,
|
||||
_id: Option<&Id>,
|
||||
operate_on_children: &mut dyn FnMut(&mut dyn Operation<T>),
|
||||
) {
|
||||
operate_on_children(self)
|
||||
}
|
||||
}
|
||||
|
||||
MoveCursor { target }
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
//! The [`pane_grid` example] showcases how to use a [`PaneGrid`] with resizing,
|
||||
//! drag and drop, and hotkey support.
|
||||
//!
|
||||
//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.4/examples/pane_grid
|
||||
//! [`pane_grid` example]: https://github.com/iced-rs/iced/tree/0.5/examples/pane_grid
|
||||
mod axis;
|
||||
mod configuration;
|
||||
mod content;
|
||||
|
|
@ -38,6 +38,7 @@ use crate::mouse;
|
|||
use crate::overlay;
|
||||
use crate::renderer;
|
||||
use crate::touch;
|
||||
use crate::widget;
|
||||
use crate::widget::container;
|
||||
use crate::widget::tree::{self, Tree};
|
||||
use crate::{
|
||||
|
|
@ -85,7 +86,7 @@ use crate::{
|
|||
/// let (mut state, _) = pane_grid::State::new(PaneState::SomePane);
|
||||
///
|
||||
/// let pane_grid =
|
||||
/// PaneGrid::new(&state, |pane, state| {
|
||||
/// PaneGrid::new(&state, |pane, state, is_maximized| {
|
||||
/// pane_grid::Content::new(match state {
|
||||
/// PaneState::SomePane => text("This is some pane"),
|
||||
/// PaneState::AnotherKindOfPane => text("This is another kind of pane"),
|
||||
|
|
@ -100,8 +101,7 @@ where
|
|||
Renderer: crate::Renderer,
|
||||
Renderer::Theme: StyleSheet + container::StyleSheet,
|
||||
{
|
||||
state: &'a state::Internal,
|
||||
elements: Vec<(Pane, Content<'a, Message, Renderer>)>,
|
||||
contents: Contents<'a, Content<'a, Message, Renderer>>,
|
||||
width: Length,
|
||||
height: Length,
|
||||
spacing: u16,
|
||||
|
|
@ -119,22 +119,35 @@ where
|
|||
/// Creates a [`PaneGrid`] with the given [`State`] and view function.
|
||||
///
|
||||
/// The view function will be called to display each [`Pane`] present in the
|
||||
/// [`State`].
|
||||
/// [`State`]. [`bool`] is set if the pane is maximized.
|
||||
pub fn new<T>(
|
||||
state: &'a State<T>,
|
||||
view: impl Fn(Pane, &'a T) -> Content<'a, Message, Renderer>,
|
||||
view: impl Fn(Pane, &'a T, bool) -> Content<'a, Message, Renderer>,
|
||||
) -> Self {
|
||||
let elements = {
|
||||
state
|
||||
.panes
|
||||
.iter()
|
||||
.map(|(pane, pane_state)| (*pane, view(*pane, pane_state)))
|
||||
.collect()
|
||||
let contents = if let Some((pane, pane_state)) =
|
||||
state.maximized.and_then(|pane| {
|
||||
state.panes.get(&pane).map(|pane_state| (pane, pane_state))
|
||||
}) {
|
||||
Contents::Maximized(
|
||||
pane,
|
||||
view(pane, pane_state, true),
|
||||
Node::Pane(pane),
|
||||
)
|
||||
} else {
|
||||
Contents::All(
|
||||
state
|
||||
.panes
|
||||
.iter()
|
||||
.map(|(pane, pane_state)| {
|
||||
(*pane, view(*pane, pane_state, false))
|
||||
})
|
||||
.collect(),
|
||||
&state.internal,
|
||||
)
|
||||
};
|
||||
|
||||
Self {
|
||||
elements,
|
||||
state: &state.internal,
|
||||
contents,
|
||||
width: Length::Fill,
|
||||
height: Length::Fill,
|
||||
spacing: 0,
|
||||
|
|
@ -208,6 +221,12 @@ where
|
|||
self.style = style.into();
|
||||
self
|
||||
}
|
||||
|
||||
fn drag_enabled(&self) -> bool {
|
||||
(!self.contents.is_maximized())
|
||||
.then(|| self.on_drag.is_some())
|
||||
.unwrap_or_default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Renderer> Widget<Message, Renderer>
|
||||
|
|
@ -225,18 +244,25 @@ where
|
|||
}
|
||||
|
||||
fn children(&self) -> Vec<Tree> {
|
||||
self.elements
|
||||
self.contents
|
||||
.iter()
|
||||
.map(|(_, content)| content.state())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn diff(&self, tree: &mut Tree) {
|
||||
tree.diff_children_custom(
|
||||
&self.elements,
|
||||
|state, (_, content)| content.diff(state),
|
||||
|(_, content)| content.state(),
|
||||
)
|
||||
match &self.contents {
|
||||
Contents::All(contents, _) => tree.diff_children_custom(
|
||||
contents,
|
||||
|state, (_, content)| content.diff(state),
|
||||
|(_, content)| content.state(),
|
||||
),
|
||||
Contents::Maximized(_, content, _) => tree.diff_children_custom(
|
||||
&[content],
|
||||
|state, content| content.diff(state),
|
||||
|content| content.state(),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn width(&self) -> Length {
|
||||
|
|
@ -255,15 +281,32 @@ where
|
|||
layout(
|
||||
renderer,
|
||||
limits,
|
||||
self.state,
|
||||
self.contents.layout(),
|
||||
self.width,
|
||||
self.height,
|
||||
self.spacing,
|
||||
self.elements.iter().map(|(pane, content)| (*pane, content)),
|
||||
|element, renderer, limits| element.layout(renderer, limits),
|
||||
self.contents.iter(),
|
||||
|content, renderer, limits| content.layout(renderer, limits),
|
||||
)
|
||||
}
|
||||
|
||||
fn operate(
|
||||
&self,
|
||||
tree: &mut Tree,
|
||||
layout: Layout<'_>,
|
||||
operation: &mut dyn widget::Operation<Message>,
|
||||
) {
|
||||
operation.container(None, &mut |operation| {
|
||||
self.contents
|
||||
.iter()
|
||||
.zip(&mut tree.children)
|
||||
.zip(layout.children())
|
||||
.for_each(|(((_pane, content), state), layout)| {
|
||||
content.operate(state, layout, operation);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
|
|
@ -276,28 +319,34 @@ where
|
|||
) -> event::Status {
|
||||
let action = tree.state.downcast_mut::<state::Action>();
|
||||
|
||||
let on_drag = if self.drag_enabled() {
|
||||
&self.on_drag
|
||||
} else {
|
||||
&None
|
||||
};
|
||||
|
||||
let event_status = update(
|
||||
action,
|
||||
self.state,
|
||||
self.contents.layout(),
|
||||
&event,
|
||||
layout,
|
||||
cursor_position,
|
||||
shell,
|
||||
self.spacing,
|
||||
self.elements.iter().map(|(pane, content)| (*pane, content)),
|
||||
self.contents.iter(),
|
||||
&self.on_click,
|
||||
&self.on_drag,
|
||||
on_drag,
|
||||
&self.on_resize,
|
||||
);
|
||||
|
||||
let picked_pane = action.picked_pane().map(|(pane, _)| pane);
|
||||
|
||||
self.elements
|
||||
self.contents
|
||||
.iter_mut()
|
||||
.zip(&mut tree.children)
|
||||
.zip(layout.children())
|
||||
.map(|(((pane, content), tree), layout)| {
|
||||
let is_picked = picked_pane == Some(*pane);
|
||||
let is_picked = picked_pane == Some(pane);
|
||||
|
||||
content.on_event(
|
||||
tree,
|
||||
|
|
@ -323,14 +372,14 @@ where
|
|||
) -> mouse::Interaction {
|
||||
mouse_interaction(
|
||||
tree.state.downcast_ref(),
|
||||
self.state,
|
||||
self.contents.layout(),
|
||||
layout,
|
||||
cursor_position,
|
||||
self.spacing,
|
||||
self.on_resize.as_ref().map(|(leeway, _)| *leeway),
|
||||
)
|
||||
.unwrap_or_else(|| {
|
||||
self.elements
|
||||
self.contents
|
||||
.iter()
|
||||
.zip(&tree.children)
|
||||
.zip(layout.children())
|
||||
|
|
@ -341,7 +390,7 @@ where
|
|||
cursor_position,
|
||||
viewport,
|
||||
renderer,
|
||||
self.on_drag.is_some(),
|
||||
self.drag_enabled(),
|
||||
)
|
||||
})
|
||||
.max()
|
||||
|
|
@ -361,7 +410,7 @@ where
|
|||
) {
|
||||
draw(
|
||||
tree.state.downcast_ref(),
|
||||
self.state,
|
||||
self.contents.layout(),
|
||||
layout,
|
||||
cursor_position,
|
||||
renderer,
|
||||
|
|
@ -370,11 +419,11 @@ where
|
|||
viewport,
|
||||
self.spacing,
|
||||
self.on_resize.as_ref().map(|(leeway, _)| *leeway),
|
||||
self.style,
|
||||
self.elements
|
||||
&self.style,
|
||||
self.contents
|
||||
.iter()
|
||||
.zip(&tree.children)
|
||||
.map(|((pane, content), tree)| (*pane, (content, tree))),
|
||||
.map(|((pane, content), tree)| (pane, (content, tree))),
|
||||
|(content, tree),
|
||||
renderer,
|
||||
style,
|
||||
|
|
@ -395,13 +444,13 @@ where
|
|||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b self,
|
||||
&'b mut self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'_, Message, Renderer>> {
|
||||
self.elements
|
||||
.iter()
|
||||
self.contents
|
||||
.iter_mut()
|
||||
.zip(&mut tree.children)
|
||||
.zip(layout.children())
|
||||
.filter_map(|(((_, pane), tree), layout)| {
|
||||
|
|
@ -429,24 +478,24 @@ where
|
|||
pub fn layout<Renderer, T>(
|
||||
renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
state: &state::Internal,
|
||||
node: &Node,
|
||||
width: Length,
|
||||
height: Length,
|
||||
spacing: u16,
|
||||
elements: impl Iterator<Item = (Pane, T)>,
|
||||
layout_element: impl Fn(T, &Renderer, &layout::Limits) -> layout::Node,
|
||||
contents: impl Iterator<Item = (Pane, T)>,
|
||||
layout_content: impl Fn(T, &Renderer, &layout::Limits) -> layout::Node,
|
||||
) -> layout::Node {
|
||||
let limits = limits.width(width).height(height);
|
||||
let size = limits.resolve(Size::ZERO);
|
||||
|
||||
let regions = state.pane_regions(f32::from(spacing), size);
|
||||
let children = elements
|
||||
.filter_map(|(pane, element)| {
|
||||
let regions = node.pane_regions(f32::from(spacing), size);
|
||||
let children = contents
|
||||
.filter_map(|(pane, content)| {
|
||||
let region = regions.get(&pane)?;
|
||||
let size = Size::new(region.width, region.height);
|
||||
|
||||
let mut node = layout_element(
|
||||
element,
|
||||
let mut node = layout_content(
|
||||
content,
|
||||
renderer,
|
||||
&layout::Limits::new(size, size),
|
||||
);
|
||||
|
|
@ -464,13 +513,13 @@ pub fn layout<Renderer, T>(
|
|||
/// accordingly.
|
||||
pub fn update<'a, Message, T: Draggable>(
|
||||
action: &mut state::Action,
|
||||
state: &state::Internal,
|
||||
node: &Node,
|
||||
event: &Event,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
spacing: u16,
|
||||
elements: impl Iterator<Item = (Pane, T)>,
|
||||
contents: impl Iterator<Item = (Pane, T)>,
|
||||
on_click: &Option<Box<dyn Fn(Pane) -> Message + 'a>>,
|
||||
on_drag: &Option<Box<dyn Fn(DragEvent) -> Message + 'a>>,
|
||||
on_resize: &Option<(u16, Box<dyn Fn(ResizeEvent) -> Message + 'a>)>,
|
||||
|
|
@ -492,7 +541,7 @@ pub fn update<'a, Message, T: Draggable>(
|
|||
cursor_position.y - bounds.y,
|
||||
);
|
||||
|
||||
let splits = state.split_regions(
|
||||
let splits = node.split_regions(
|
||||
f32::from(spacing),
|
||||
Size::new(bounds.width, bounds.height),
|
||||
);
|
||||
|
|
@ -514,7 +563,7 @@ pub fn update<'a, Message, T: Draggable>(
|
|||
layout,
|
||||
cursor_position,
|
||||
shell,
|
||||
elements,
|
||||
contents,
|
||||
on_click,
|
||||
on_drag,
|
||||
);
|
||||
|
|
@ -526,7 +575,7 @@ pub fn update<'a, Message, T: Draggable>(
|
|||
layout,
|
||||
cursor_position,
|
||||
shell,
|
||||
elements,
|
||||
contents,
|
||||
on_click,
|
||||
on_drag,
|
||||
);
|
||||
|
|
@ -539,7 +588,7 @@ pub fn update<'a, Message, T: Draggable>(
|
|||
| Event::Touch(touch::Event::FingerLost { .. }) => {
|
||||
if let Some((pane, _)) = action.picked_pane() {
|
||||
if let Some(on_drag) = on_drag {
|
||||
let mut dropped_region = elements
|
||||
let mut dropped_region = contents
|
||||
.zip(layout.children())
|
||||
.filter(|(_, layout)| {
|
||||
layout.bounds().contains(cursor_position)
|
||||
|
|
@ -570,7 +619,7 @@ pub fn update<'a, Message, T: Draggable>(
|
|||
if let Some((split, _)) = action.picked_split() {
|
||||
let bounds = layout.bounds();
|
||||
|
||||
let splits = state.split_regions(
|
||||
let splits = node.split_regions(
|
||||
f32::from(spacing),
|
||||
Size::new(bounds.width, bounds.height),
|
||||
);
|
||||
|
|
@ -609,13 +658,13 @@ fn click_pane<'a, Message, T>(
|
|||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
shell: &mut Shell<'_, Message>,
|
||||
elements: impl Iterator<Item = (Pane, T)>,
|
||||
contents: impl Iterator<Item = (Pane, T)>,
|
||||
on_click: &Option<Box<dyn Fn(Pane) -> Message + 'a>>,
|
||||
on_drag: &Option<Box<dyn Fn(DragEvent) -> Message + 'a>>,
|
||||
) where
|
||||
T: Draggable,
|
||||
{
|
||||
let mut clicked_region = elements
|
||||
let mut clicked_region = contents
|
||||
.zip(layout.children())
|
||||
.filter(|(_, layout)| layout.bounds().contains(cursor_position));
|
||||
|
||||
|
|
@ -642,7 +691,7 @@ fn click_pane<'a, Message, T>(
|
|||
/// Returns the current [`mouse::Interaction`] of a [`PaneGrid`].
|
||||
pub fn mouse_interaction(
|
||||
action: &state::Action,
|
||||
state: &state::Internal,
|
||||
node: &Node,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
spacing: u16,
|
||||
|
|
@ -658,7 +707,7 @@ pub fn mouse_interaction(
|
|||
let bounds = layout.bounds();
|
||||
|
||||
let splits =
|
||||
state.split_regions(f32::from(spacing), bounds.size());
|
||||
node.split_regions(f32::from(spacing), bounds.size());
|
||||
|
||||
let relative_cursor = Point::new(
|
||||
cursor_position.x - bounds.x,
|
||||
|
|
@ -687,7 +736,7 @@ pub fn mouse_interaction(
|
|||
/// Draws a [`PaneGrid`].
|
||||
pub fn draw<Renderer, T>(
|
||||
action: &state::Action,
|
||||
state: &state::Internal,
|
||||
node: &Node,
|
||||
layout: Layout<'_>,
|
||||
cursor_position: Point,
|
||||
renderer: &mut Renderer,
|
||||
|
|
@ -696,8 +745,8 @@ pub fn draw<Renderer, T>(
|
|||
viewport: &Rectangle,
|
||||
spacing: u16,
|
||||
resize_leeway: Option<u16>,
|
||||
style: <Renderer::Theme as StyleSheet>::Style,
|
||||
elements: impl Iterator<Item = (Pane, T)>,
|
||||
style: &<Renderer::Theme as StyleSheet>::Style,
|
||||
contents: impl Iterator<Item = (Pane, T)>,
|
||||
draw_pane: impl Fn(
|
||||
T,
|
||||
&mut Renderer,
|
||||
|
|
@ -717,7 +766,7 @@ pub fn draw<Renderer, T>(
|
|||
.and_then(|(split, axis)| {
|
||||
let bounds = layout.bounds();
|
||||
|
||||
let splits = state.split_regions(f32::from(spacing), bounds.size());
|
||||
let splits = node.split_regions(f32::from(spacing), bounds.size());
|
||||
|
||||
let (_axis, region, ratio) = splits.get(&split)?;
|
||||
|
||||
|
|
@ -736,7 +785,7 @@ pub fn draw<Renderer, T>(
|
|||
);
|
||||
|
||||
let splits =
|
||||
state.split_regions(f32::from(spacing), bounds.size());
|
||||
node.split_regions(f32::from(spacing), bounds.size());
|
||||
|
||||
let (_split, axis, region) = hovered_split(
|
||||
splits.iter(),
|
||||
|
|
@ -759,7 +808,7 @@ pub fn draw<Renderer, T>(
|
|||
|
||||
let mut render_picked_pane = None;
|
||||
|
||||
for ((id, pane), layout) in elements.zip(layout.children()) {
|
||||
for ((id, pane), layout) in contents.zip(layout.children()) {
|
||||
match picked_pane {
|
||||
Some((dragging, origin)) if id == dragging => {
|
||||
render_picked_pane = Some((pane, origin, layout));
|
||||
|
|
@ -897,3 +946,49 @@ fn hovered_split<'a>(
|
|||
})
|
||||
.next()
|
||||
}
|
||||
|
||||
/// The visible contents of the [`PaneGrid`]
|
||||
#[derive(Debug)]
|
||||
pub enum Contents<'a, T> {
|
||||
/// All panes are visible
|
||||
All(Vec<(Pane, T)>, &'a state::Internal),
|
||||
/// A maximized pane is visible
|
||||
Maximized(Pane, T, Node),
|
||||
}
|
||||
|
||||
impl<'a, T> Contents<'a, T> {
|
||||
/// Returns the layout [`Node`] of the [`Contents`]
|
||||
pub fn layout(&self) -> &Node {
|
||||
match self {
|
||||
Contents::All(_, state) => state.layout(),
|
||||
Contents::Maximized(_, _, layout) => layout,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns an iterator over the values of the [`Contents`]
|
||||
pub fn iter(&self) -> Box<dyn Iterator<Item = (Pane, &T)> + '_> {
|
||||
match self {
|
||||
Contents::All(contents, _) => Box::new(
|
||||
contents.iter().map(|(pane, content)| (*pane, content)),
|
||||
),
|
||||
Contents::Maximized(pane, content, _) => {
|
||||
Box::new(std::iter::once((*pane, content)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn iter_mut(&mut self) -> Box<dyn Iterator<Item = (Pane, &mut T)> + '_> {
|
||||
match self {
|
||||
Contents::All(contents, _) => Box::new(
|
||||
contents.iter_mut().map(|(pane, content)| (*pane, content)),
|
||||
),
|
||||
Contents::Maximized(pane, content, _) => {
|
||||
Box::new(std::iter::once((*pane, content)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_maximized(&self) -> bool {
|
||||
matches!(self, Self::Maximized(..))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::overlay;
|
|||
use crate::renderer;
|
||||
use crate::widget::container;
|
||||
use crate::widget::pane_grid::{Draggable, TitleBar};
|
||||
use crate::widget::Tree;
|
||||
use crate::widget::{self, Tree};
|
||||
use crate::{Clipboard, Element, Layout, Point, Rectangle, Shell, Size};
|
||||
|
||||
/// The content of a [`Pane`].
|
||||
|
|
@ -87,7 +87,7 @@ where
|
|||
|
||||
/// Draws the [`Content`] with the provided [`Renderer`] and [`Layout`].
|
||||
///
|
||||
/// [`Renderer`]: iced_native::Renderer
|
||||
/// [`Renderer`]: crate::Renderer
|
||||
pub fn draw(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
|
|
@ -103,7 +103,7 @@ where
|
|||
let bounds = layout.bounds();
|
||||
|
||||
{
|
||||
let style = theme.appearance(self.style);
|
||||
let style = theme.appearance(&self.style);
|
||||
|
||||
container::draw_background(renderer, &style, bounds);
|
||||
}
|
||||
|
|
@ -183,6 +183,33 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn operate(
|
||||
&self,
|
||||
tree: &mut Tree,
|
||||
layout: Layout<'_>,
|
||||
operation: &mut dyn widget::Operation<Message>,
|
||||
) {
|
||||
let body_layout = if let Some(title_bar) = &self.title_bar {
|
||||
let mut children = layout.children();
|
||||
|
||||
title_bar.operate(
|
||||
&mut tree.children[1],
|
||||
children.next().unwrap(),
|
||||
operation,
|
||||
);
|
||||
|
||||
children.next().unwrap()
|
||||
} else {
|
||||
layout
|
||||
};
|
||||
|
||||
self.body.as_widget().operate(
|
||||
&mut tree.children[0],
|
||||
body_layout,
|
||||
operation,
|
||||
);
|
||||
}
|
||||
|
||||
pub(crate) fn on_event(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
|
|
@ -278,12 +305,12 @@ where
|
|||
}
|
||||
|
||||
pub(crate) fn overlay<'b>(
|
||||
&'b self,
|
||||
&'b mut self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
||||
if let Some(title_bar) = self.title_bar.as_ref() {
|
||||
if let Some(title_bar) = self.title_bar.as_mut() {
|
||||
let mut children = layout.children();
|
||||
let title_bar_layout = children.next()?;
|
||||
|
||||
|
|
@ -294,14 +321,14 @@ where
|
|||
match title_bar.overlay(title_bar_state, title_bar_layout, renderer)
|
||||
{
|
||||
Some(overlay) => Some(overlay),
|
||||
None => self.body.as_widget().overlay(
|
||||
None => self.body.as_widget_mut().overlay(
|
||||
body_state,
|
||||
children.next()?,
|
||||
renderer,
|
||||
),
|
||||
}
|
||||
} else {
|
||||
self.body.as_widget().overlay(
|
||||
self.body.as_widget_mut().overlay(
|
||||
&mut tree.children[0],
|
||||
layout,
|
||||
renderer,
|
||||
|
|
|
|||
|
|
@ -4,9 +4,9 @@
|
|||
use crate::widget::pane_grid::{
|
||||
Axis, Configuration, Direction, Node, Pane, Split,
|
||||
};
|
||||
use crate::{Point, Rectangle, Size};
|
||||
use crate::{Point, Size};
|
||||
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::collections::HashMap;
|
||||
|
||||
/// The state of a [`PaneGrid`].
|
||||
///
|
||||
|
|
@ -31,6 +31,11 @@ pub struct State<T> {
|
|||
///
|
||||
/// [`PaneGrid`]: crate::widget::PaneGrid
|
||||
pub internal: Internal,
|
||||
|
||||
/// The maximized [`Pane`] of the [`PaneGrid`].
|
||||
///
|
||||
/// [`PaneGrid`]: crate::widget::PaneGrid
|
||||
pub(super) maximized: Option<Pane>,
|
||||
}
|
||||
|
||||
impl<T> State<T> {
|
||||
|
|
@ -52,7 +57,11 @@ impl<T> State<T> {
|
|||
let internal =
|
||||
Internal::from_configuration(&mut panes, config.into(), 0);
|
||||
|
||||
State { panes, internal }
|
||||
State {
|
||||
panes,
|
||||
internal,
|
||||
maximized: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the total amount of panes in the [`State`].
|
||||
|
|
@ -153,6 +162,7 @@ impl<T> State<T> {
|
|||
node.split(new_split, axis, new_pane);
|
||||
|
||||
let _ = self.panes.insert(new_pane, state);
|
||||
let _ = self.maximized.take();
|
||||
|
||||
Some((new_pane, new_split))
|
||||
}
|
||||
|
|
@ -194,12 +204,39 @@ impl<T> State<T> {
|
|||
/// Closes the given [`Pane`] and returns its internal state and its closest
|
||||
/// sibling, if it exists.
|
||||
pub fn close(&mut self, pane: &Pane) -> Option<(T, Pane)> {
|
||||
if self.maximized == Some(*pane) {
|
||||
let _ = self.maximized.take();
|
||||
}
|
||||
|
||||
if let Some(sibling) = self.internal.layout.remove(pane) {
|
||||
self.panes.remove(pane).map(|state| (state, sibling))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Maximize the given [`Pane`]. Only this pane will be rendered by the
|
||||
/// [`PaneGrid`] until [`Self::restore()`] is called.
|
||||
///
|
||||
/// [`PaneGrid`]: crate::widget::PaneGrid
|
||||
pub fn maximize(&mut self, pane: &Pane) {
|
||||
self.maximized = Some(*pane);
|
||||
}
|
||||
|
||||
/// Restore the currently maximized [`Pane`] to it's normal size. All panes
|
||||
/// will be rendered by the [`PaneGrid`].
|
||||
///
|
||||
/// [`PaneGrid`]: crate::widget::PaneGrid
|
||||
pub fn restore(&mut self) {
|
||||
let _ = self.maximized.take();
|
||||
}
|
||||
|
||||
/// Returns the maximized [`Pane`] of the [`PaneGrid`].
|
||||
///
|
||||
/// [`PaneGrid`]: crate::widget::PaneGrid
|
||||
pub fn maximized(&self) -> Option<Pane> {
|
||||
self.maximized
|
||||
}
|
||||
}
|
||||
|
||||
/// The internal state of a [`PaneGrid`].
|
||||
|
|
@ -226,11 +263,13 @@ impl Internal {
|
|||
let Internal {
|
||||
layout: a,
|
||||
last_id: next_id,
|
||||
..
|
||||
} = Self::from_configuration(panes, *a, next_id);
|
||||
|
||||
let Internal {
|
||||
layout: b,
|
||||
last_id: next_id,
|
||||
..
|
||||
} = Self::from_configuration(panes, *b, next_id);
|
||||
|
||||
(
|
||||
|
|
@ -304,25 +343,8 @@ impl Action {
|
|||
}
|
||||
|
||||
impl Internal {
|
||||
/// Calculates the current [`Pane`] regions from the [`PaneGrid`] layout.
|
||||
///
|
||||
/// [`PaneGrid`]: crate::widget::PaneGrid
|
||||
pub fn pane_regions(
|
||||
&self,
|
||||
spacing: f32,
|
||||
size: Size,
|
||||
) -> BTreeMap<Pane, Rectangle> {
|
||||
self.layout.pane_regions(spacing, size)
|
||||
}
|
||||
|
||||
/// Calculates the current [`Split`] regions from the [`PaneGrid`] layout.
|
||||
///
|
||||
/// [`PaneGrid`]: crate::widget::PaneGrid
|
||||
pub fn split_regions(
|
||||
&self,
|
||||
spacing: f32,
|
||||
size: Size,
|
||||
) -> BTreeMap<Split, (Axis, Rectangle, f32)> {
|
||||
self.layout.split_regions(spacing, size)
|
||||
/// The layout [`Node`] of the [`Internal`] state
|
||||
pub fn layout(&self) -> &Node {
|
||||
&self.layout
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use crate::mouse;
|
|||
use crate::overlay;
|
||||
use crate::renderer;
|
||||
use crate::widget::container;
|
||||
use crate::widget::Tree;
|
||||
use crate::widget::{self, Tree};
|
||||
use crate::{
|
||||
Clipboard, Element, Layout, Padding, Point, Rectangle, Shell, Size,
|
||||
};
|
||||
|
|
@ -114,7 +114,7 @@ where
|
|||
|
||||
/// Draws the [`TitleBar`] with the provided [`Renderer`] and [`Layout`].
|
||||
///
|
||||
/// [`Renderer`]: iced_native::Renderer
|
||||
/// [`Renderer`]: crate::Renderer
|
||||
pub fn draw(
|
||||
&self,
|
||||
tree: &Tree,
|
||||
|
|
@ -129,7 +129,7 @@ where
|
|||
use container::StyleSheet;
|
||||
|
||||
let bounds = layout.bounds();
|
||||
let style = theme.appearance(self.style);
|
||||
let style = theme.appearance(&self.style);
|
||||
let inherited_style = renderer::Style {
|
||||
text_color: style.text_color.unwrap_or(inherited_style.text_color),
|
||||
};
|
||||
|
|
@ -257,6 +257,44 @@ where
|
|||
layout::Node::with_children(node.size().pad(self.padding), vec![node])
|
||||
}
|
||||
|
||||
pub(crate) fn operate(
|
||||
&self,
|
||||
tree: &mut Tree,
|
||||
layout: Layout<'_>,
|
||||
operation: &mut dyn widget::Operation<Message>,
|
||||
) {
|
||||
let mut children = layout.children();
|
||||
let padded = children.next().unwrap();
|
||||
|
||||
let mut children = padded.children();
|
||||
let title_layout = children.next().unwrap();
|
||||
let mut show_title = true;
|
||||
|
||||
if let Some(controls) = &self.controls {
|
||||
let controls_layout = children.next().unwrap();
|
||||
|
||||
if title_layout.bounds().width + controls_layout.bounds().width
|
||||
> padded.bounds().width
|
||||
{
|
||||
show_title = false;
|
||||
}
|
||||
|
||||
controls.as_widget().operate(
|
||||
&mut tree.children[1],
|
||||
controls_layout,
|
||||
operation,
|
||||
)
|
||||
};
|
||||
|
||||
if show_title {
|
||||
self.content.as_widget().operate(
|
||||
&mut tree.children[0],
|
||||
title_layout,
|
||||
operation,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn on_event(
|
||||
&mut self,
|
||||
tree: &mut Tree,
|
||||
|
|
@ -357,7 +395,7 @@ where
|
|||
}
|
||||
|
||||
pub(crate) fn overlay<'b>(
|
||||
&'b self,
|
||||
&'b mut self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
|
|
@ -377,13 +415,13 @@ where
|
|||
let controls_state = states.next().unwrap();
|
||||
|
||||
content
|
||||
.as_widget()
|
||||
.as_widget_mut()
|
||||
.overlay(title_state, title_layout, renderer)
|
||||
.or_else(move || {
|
||||
controls.as_ref().and_then(|controls| {
|
||||
controls.as_mut().and_then(|controls| {
|
||||
let controls_layout = children.next()?;
|
||||
|
||||
controls.as_widget().overlay(
|
||||
controls.as_widget_mut().overlay(
|
||||
controls_state,
|
||||
controls_layout,
|
||||
renderer,
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ use crate::overlay::menu::{self, Menu};
|
|||
use crate::renderer;
|
||||
use crate::text::{self, Text};
|
||||
use crate::touch;
|
||||
use crate::widget::container;
|
||||
use crate::widget::scrollable;
|
||||
use crate::widget::tree::{self, Tree};
|
||||
use crate::{
|
||||
Clipboard, Element, Layout, Length, Padding, Point, Rectangle, Shell, Size,
|
||||
|
|
@ -42,7 +44,12 @@ where
|
|||
T: ToString + Eq,
|
||||
[T]: ToOwned<Owned = Vec<T>>,
|
||||
Renderer: text::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
Renderer::Theme: StyleSheet
|
||||
+ scrollable::StyleSheet
|
||||
+ menu::StyleSheet
|
||||
+ container::StyleSheet,
|
||||
<Renderer::Theme as menu::StyleSheet>::Style:
|
||||
From<<Renderer::Theme as StyleSheet>::Style>,
|
||||
{
|
||||
/// The default padding of a [`PickList`].
|
||||
pub const DEFAULT_PADDING: Padding = Padding::new(5);
|
||||
|
|
@ -114,7 +121,12 @@ where
|
|||
[T]: ToOwned<Owned = Vec<T>>,
|
||||
Message: 'a,
|
||||
Renderer: text::Renderer + 'a,
|
||||
Renderer::Theme: StyleSheet,
|
||||
Renderer::Theme: StyleSheet
|
||||
+ scrollable::StyleSheet
|
||||
+ menu::StyleSheet
|
||||
+ container::StyleSheet,
|
||||
<Renderer::Theme as menu::StyleSheet>::Style:
|
||||
From<<Renderer::Theme as StyleSheet>::Style>,
|
||||
{
|
||||
fn tag(&self) -> tree::Tag {
|
||||
tree::Tag::of::<State<T>>()
|
||||
|
|
@ -202,12 +214,12 @@ where
|
|||
&self.font,
|
||||
self.placeholder.as_deref(),
|
||||
self.selected.as_ref(),
|
||||
self.style,
|
||||
&self.style,
|
||||
)
|
||||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b self,
|
||||
&'b mut self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
_renderer: &Renderer,
|
||||
|
|
@ -221,7 +233,7 @@ where
|
|||
self.text_size,
|
||||
self.font.clone(),
|
||||
&self.options,
|
||||
self.style,
|
||||
self.style.clone(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -233,7 +245,12 @@ where
|
|||
[T]: ToOwned<Owned = Vec<T>>,
|
||||
Message: 'a,
|
||||
Renderer: text::Renderer + 'a,
|
||||
Renderer::Theme: StyleSheet,
|
||||
Renderer::Theme: StyleSheet
|
||||
+ scrollable::StyleSheet
|
||||
+ menu::StyleSheet
|
||||
+ container::StyleSheet,
|
||||
<Renderer::Theme as menu::StyleSheet>::Style:
|
||||
From<<Renderer::Theme as StyleSheet>::Style>,
|
||||
{
|
||||
fn from(pick_list: PickList<'a, T, Message, Renderer>) -> Self {
|
||||
Self::new(pick_list)
|
||||
|
|
@ -456,7 +473,12 @@ where
|
|||
T: Clone + ToString,
|
||||
Message: 'a,
|
||||
Renderer: text::Renderer + 'a,
|
||||
Renderer::Theme: StyleSheet,
|
||||
Renderer::Theme: StyleSheet
|
||||
+ scrollable::StyleSheet
|
||||
+ menu::StyleSheet
|
||||
+ container::StyleSheet,
|
||||
<Renderer::Theme as menu::StyleSheet>::Style:
|
||||
From<<Renderer::Theme as StyleSheet>::Style>,
|
||||
{
|
||||
if state.is_open {
|
||||
let bounds = layout.bounds();
|
||||
|
|
@ -493,7 +515,7 @@ pub fn draw<T, Renderer>(
|
|||
font: &Renderer::Font,
|
||||
placeholder: Option<&str>,
|
||||
selected: Option<&T>,
|
||||
style: <Renderer::Theme as StyleSheet>::Style,
|
||||
style: &<Renderer::Theme as StyleSheet>::Style,
|
||||
) where
|
||||
Renderer: text::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
|
|
|
|||
|
|
@ -124,7 +124,7 @@ where
|
|||
/ (range_end - range_start)
|
||||
};
|
||||
|
||||
let style = theme.appearance(self.style);
|
||||
let style = theme.appearance(&self.style);
|
||||
|
||||
renderer.fill_quad(
|
||||
renderer::Quad {
|
||||
|
|
|
|||
|
|
@ -230,9 +230,9 @@ where
|
|||
let mut children = layout.children();
|
||||
|
||||
let custom_style = if is_mouse_over {
|
||||
theme.hovered(self.style, self.is_selected)
|
||||
theme.hovered(&self.style, self.is_selected)
|
||||
} else {
|
||||
theme.active(self.style, self.is_selected)
|
||||
theme.active(&self.style, self.is_selected)
|
||||
};
|
||||
|
||||
{
|
||||
|
|
|
|||
|
|
@ -229,12 +229,12 @@ where
|
|||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b self,
|
||||
&'b mut self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
||||
overlay::from_children(&self.children, tree, layout, renderer)
|
||||
overlay::from_children(&mut self.children, tree, layout, renderer)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ where
|
|||
_viewport: &Rectangle,
|
||||
) {
|
||||
let bounds = layout.bounds();
|
||||
let style = theme.style(self.style);
|
||||
let style = theme.appearance(&self.style);
|
||||
|
||||
let bounds = if self.is_horizontal {
|
||||
let line_y = (bounds.y + (bounds.height / 2.0)
|
||||
|
|
|
|||
|
|
@ -233,7 +233,7 @@ where
|
|||
self.scrollbar_width,
|
||||
self.scrollbar_margin,
|
||||
self.scroller_width,
|
||||
self.style,
|
||||
&self.style,
|
||||
|renderer, layout, cursor_position, viewport| {
|
||||
self.content.as_widget().draw(
|
||||
&tree.children[0],
|
||||
|
|
@ -276,13 +276,13 @@ where
|
|||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b self,
|
||||
&'b mut self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
||||
self.content
|
||||
.as_widget()
|
||||
.as_widget_mut()
|
||||
.overlay(
|
||||
&mut tree.children[0],
|
||||
layout.children().next().unwrap(),
|
||||
|
|
@ -334,6 +334,12 @@ impl Id {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Id> for widget::Id {
|
||||
fn from(id: Id) -> Self {
|
||||
id.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Produces a [`Command`] that snaps the [`Scrollable`] with the given [`Id`]
|
||||
/// to the provided `percentage`.
|
||||
pub fn snap_to<Message: 'static>(id: Id, percentage: f32) -> Command<Message> {
|
||||
|
|
@ -627,7 +633,7 @@ pub fn draw<Renderer>(
|
|||
scrollbar_width: u16,
|
||||
scrollbar_margin: u16,
|
||||
scroller_width: u16,
|
||||
style: <Renderer::Theme as StyleSheet>::Style,
|
||||
style: &<Renderer::Theme as StyleSheet>::Style,
|
||||
draw_content: impl FnOnce(&mut Renderer, Layout<'_>, Point, &Rectangle),
|
||||
) where
|
||||
Renderer: crate::Renderer,
|
||||
|
|
|
|||
|
|
@ -222,7 +222,7 @@ where
|
|||
self.value,
|
||||
&self.range,
|
||||
theme,
|
||||
self.style,
|
||||
&self.style,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -353,7 +353,7 @@ pub fn draw<T, R>(
|
|||
value: T,
|
||||
range: &RangeInclusive<T>,
|
||||
style_sheet: &dyn StyleSheet<Style = <R::Theme as StyleSheet>::Style>,
|
||||
style: <R::Theme as StyleSheet>::Style,
|
||||
style: &<R::Theme as StyleSheet>::Style,
|
||||
) where
|
||||
T: Into<f64> + Copy,
|
||||
R: crate::Renderer,
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ where
|
|||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
// The raw w/h of the underlying image
|
||||
let (width, height) = renderer.dimensions(&self.handle);
|
||||
let Size { width, height } = renderer.dimensions(&self.handle);
|
||||
let image_size = Size::new(width as f32, height as f32);
|
||||
|
||||
// The size to be available to the widget prior to `Shrink`ing
|
||||
|
|
@ -120,7 +120,7 @@ where
|
|||
_cursor_position: Point,
|
||||
_viewport: &Rectangle,
|
||||
) {
|
||||
let (width, height) = renderer.dimensions(&self.handle);
|
||||
let Size { width, height } = renderer.dimensions(&self.handle);
|
||||
let image_size = Size::new(width as f32, height as f32);
|
||||
|
||||
let bounds = layout.bounds();
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ where
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the [`Color`] of the [`Text`].
|
||||
/// Sets the style of the [`Text`].
|
||||
pub fn style(
|
||||
mut self,
|
||||
style: impl Into<<Renderer::Theme as StyleSheet>::Style>,
|
||||
|
|
|
|||
|
|
@ -165,7 +165,7 @@ where
|
|||
}
|
||||
|
||||
/// Draws the [`TextInput`] with the given [`Renderer`], overriding its
|
||||
/// [`text_input::Value`] if provided.
|
||||
/// [`Value`] if provided.
|
||||
///
|
||||
/// [`Renderer`]: text::Renderer
|
||||
pub fn draw(
|
||||
|
|
@ -188,7 +188,7 @@ where
|
|||
self.size,
|
||||
&self.font,
|
||||
self.is_secure,
|
||||
self.style,
|
||||
&self.style,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -233,6 +233,7 @@ where
|
|||
let state = tree.state.downcast_mut::<State>();
|
||||
|
||||
operation.focusable(state, self.id.as_ref().map(|id| &id.0));
|
||||
operation.text_input(state, self.id.as_ref().map(|id| &id.0));
|
||||
}
|
||||
|
||||
fn on_event(
|
||||
|
|
@ -284,7 +285,7 @@ where
|
|||
self.size,
|
||||
&self.font,
|
||||
self.is_secure,
|
||||
self.style,
|
||||
&self.style,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -332,11 +333,43 @@ impl Id {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Id> for widget::Id {
|
||||
fn from(id: Id) -> Self {
|
||||
id.0
|
||||
}
|
||||
}
|
||||
|
||||
/// Produces a [`Command`] that focuses the [`TextInput`] with the given [`Id`].
|
||||
pub fn focus<Message: 'static>(id: Id) -> Command<Message> {
|
||||
Command::widget(operation::focusable::focus(id.0))
|
||||
}
|
||||
|
||||
/// Produces a [`Command`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the
|
||||
/// end.
|
||||
pub fn move_cursor_to_end<Message: 'static>(id: Id) -> Command<Message> {
|
||||
Command::widget(operation::text_input::move_cursor_to_end(id.0))
|
||||
}
|
||||
|
||||
/// Produces a [`Command`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the
|
||||
/// front.
|
||||
pub fn move_cursor_to_front<Message: 'static>(id: Id) -> Command<Message> {
|
||||
Command::widget(operation::text_input::move_cursor_to_front(id.0))
|
||||
}
|
||||
|
||||
/// Produces a [`Command`] that moves the cursor of the [`TextInput`] with the given [`Id`] to the
|
||||
/// provided position.
|
||||
pub fn move_cursor_to<Message: 'static>(
|
||||
id: Id,
|
||||
position: usize,
|
||||
) -> Command<Message> {
|
||||
Command::widget(operation::text_input::move_cursor_to(id.0, position))
|
||||
}
|
||||
|
||||
/// Produces a [`Command`] that selects all the content of the [`TextInput`] with the given [`Id`].
|
||||
pub fn select_all<Message: 'static>(id: Id) -> Command<Message> {
|
||||
Command::widget(operation::text_input::select_all(id.0))
|
||||
}
|
||||
|
||||
/// Computes the layout of a [`TextInput`].
|
||||
pub fn layout<Renderer>(
|
||||
renderer: &Renderer,
|
||||
|
|
@ -350,6 +383,8 @@ where
|
|||
{
|
||||
let text_size = size.unwrap_or_else(|| renderer.default_size());
|
||||
|
||||
let padding = padding.fit(Size::ZERO, limits.max());
|
||||
|
||||
let limits = limits
|
||||
.pad(padding)
|
||||
.width(width)
|
||||
|
|
@ -742,7 +777,7 @@ pub fn draw<Renderer>(
|
|||
size: Option<u16>,
|
||||
font: &Renderer::Font,
|
||||
is_secure: bool,
|
||||
style: <Renderer::Theme as StyleSheet>::Style,
|
||||
style: &<Renderer::Theme as StyleSheet>::Style,
|
||||
) where
|
||||
Renderer: text::Renderer,
|
||||
Renderer::Theme: StyleSheet,
|
||||
|
|
@ -997,6 +1032,24 @@ impl operation::Focusable for State {
|
|||
}
|
||||
}
|
||||
|
||||
impl operation::TextInput for State {
|
||||
fn move_cursor_to_front(&mut self) {
|
||||
State::move_cursor_to_front(self)
|
||||
}
|
||||
|
||||
fn move_cursor_to_end(&mut self) {
|
||||
State::move_cursor_to_end(self)
|
||||
}
|
||||
|
||||
fn move_cursor_to(&mut self, position: usize) {
|
||||
State::move_cursor_to(self, position)
|
||||
}
|
||||
|
||||
fn select_all(&mut self) {
|
||||
State::select_all(self)
|
||||
}
|
||||
}
|
||||
|
||||
mod platform {
|
||||
use crate::keyboard;
|
||||
|
||||
|
|
|
|||
|
|
@ -260,9 +260,9 @@ where
|
|||
let is_mouse_over = bounds.contains(cursor_position);
|
||||
|
||||
let style = if is_mouse_over {
|
||||
theme.hovered(self.style, self.is_active)
|
||||
theme.hovered(&self.style, self.is_active)
|
||||
} else {
|
||||
theme.active(self.style, self.is_active)
|
||||
theme.active(&self.style, self.is_active)
|
||||
};
|
||||
|
||||
let border_radius = bounds.height as f32 / BORDER_RADIUS_RATIO;
|
||||
|
|
|
|||
|
|
@ -201,7 +201,7 @@ where
|
|||
self.gap,
|
||||
self.padding,
|
||||
self.snap_within_viewport,
|
||||
self.style,
|
||||
&self.style,
|
||||
|renderer, limits| {
|
||||
Widget::<(), Renderer>::layout(tooltip, renderer, limits)
|
||||
},
|
||||
|
|
@ -221,12 +221,12 @@ where
|
|||
}
|
||||
|
||||
fn overlay<'b>(
|
||||
&'b self,
|
||||
&'b mut self,
|
||||
tree: &'b mut Tree,
|
||||
layout: Layout<'_>,
|
||||
renderer: &Renderer,
|
||||
) -> Option<overlay::Element<'b, Message, Renderer>> {
|
||||
self.content.as_widget().overlay(
|
||||
self.content.as_widget_mut().overlay(
|
||||
&mut tree.children[0],
|
||||
layout,
|
||||
renderer,
|
||||
|
|
@ -275,7 +275,7 @@ pub fn draw<Renderer>(
|
|||
gap: u16,
|
||||
padding: u16,
|
||||
snap_within_viewport: bool,
|
||||
style: <Renderer::Theme as container::StyleSheet>::Style,
|
||||
style: &<Renderer::Theme as container::StyleSheet>::Style,
|
||||
layout_text: impl FnOnce(&Renderer, &layout::Limits) -> layout::Node,
|
||||
draw_text: impl FnOnce(
|
||||
&mut Renderer,
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ impl Tree {
|
|||
}
|
||||
}
|
||||
|
||||
/// Creates a new [`Tree`] for the provided [`Element`].
|
||||
/// Creates a new [`Tree`] for the provided [`Widget`].
|
||||
pub fn new<'a, Message, Renderer>(
|
||||
widget: impl Borrow<dyn Widget<Message, Renderer> + 'a>,
|
||||
) -> Self
|
||||
|
|
@ -46,10 +46,10 @@ impl Tree {
|
|||
}
|
||||
}
|
||||
|
||||
/// Reconciliates the current tree with the provided [`Element`].
|
||||
/// Reconciliates the current tree with the provided [`Widget`].
|
||||
///
|
||||
/// If the tag of the [`Element`] matches the tag of the [`Tree`], then the
|
||||
/// [`Element`] proceeds with the reconciliation (i.e. [`Widget::diff`] is called).
|
||||
/// If the tag of the [`Widget`] matches the tag of the [`Tree`], then the
|
||||
/// [`Widget`] proceeds with the reconciliation (i.e. [`Widget::diff`] is called).
|
||||
///
|
||||
/// Otherwise, the whole [`Tree`] is recreated.
|
||||
///
|
||||
|
|
@ -67,7 +67,7 @@ impl Tree {
|
|||
}
|
||||
}
|
||||
|
||||
/// Reconciliates the children of the tree with the provided list of [`Element`].
|
||||
/// Reconciliates the children of the tree with the provided list of widgets.
|
||||
pub fn diff_children<'a, Message, Renderer>(
|
||||
&mut self,
|
||||
new_children: &[impl Borrow<dyn Widget<Message, Renderer> + 'a>],
|
||||
|
|
@ -81,7 +81,7 @@ impl Tree {
|
|||
)
|
||||
}
|
||||
|
||||
/// Reconciliates the children of the tree with the provided list of [`Element`] using custom
|
||||
/// Reconciliates the children of the tree with the provided list of widgets using custom
|
||||
/// logic both for diffing and creating new widget state.
|
||||
pub fn diff_children_custom<T>(
|
||||
&mut self,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue