summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--examples/multi_window/src/main.rs50
-rw-r--r--glutin/src/multi_window.rs116
-rw-r--r--native/src/window.rs3
-rw-r--r--winit/src/multi_window.rs117
4 files changed, 171 insertions, 115 deletions
diff --git a/examples/multi_window/src/main.rs b/examples/multi_window/src/main.rs
index 0d0a809b..23f08217 100644
--- a/examples/multi_window/src/main.rs
+++ b/examples/multi_window/src/main.rs
@@ -12,6 +12,7 @@ use iced::{Color, Command, Element, Length, Settings, Size, Subscription};
use iced_lazy::responsive;
use iced_native::{event, subscription, Event};
+use iced_native::widget::scrollable::{Properties, RelativeOffset};
use iced_native::window::Id;
use std::collections::HashMap;
@@ -56,6 +57,7 @@ enum WindowMessage {
CloseFocused,
SelectedWindow(pane_grid::Pane, SelectableWindow),
CloseWindow,
+ SnapToggle,
}
impl Application for Example {
@@ -94,6 +96,25 @@ impl Application for Example {
fn update(&mut self, message: Message) -> Command<Message> {
let Message::Window(id, message) = message;
match message {
+ WindowMessage::SnapToggle => {
+ let window = self.windows.get_mut(&id).unwrap();
+
+ if let Some(focused) = &window.focus {
+ let pane = window.panes.get_mut(focused).unwrap();
+
+ let cmd = scrollable::snap_to(
+ pane.scrollable_id.clone(),
+ if pane.snapped {
+ RelativeOffset::START
+ } else {
+ RelativeOffset::END
+ },
+ );
+
+ pane.snapped = !pane.snapped;
+ return cmd;
+ }
+ }
WindowMessage::Split(axis, pane) => {
let window = self.windows.get_mut(&id).unwrap();
let result = window.panes.split(
@@ -311,7 +332,13 @@ impl Application for Example {
});
pane_grid::Content::new(responsive(move |size| {
- view_content(id, total_panes, pane.is_pinned, size)
+ view_content(
+ id,
+ pane.scrollable_id.clone(),
+ total_panes,
+ pane.is_pinned,
+ size,
+ )
}))
.title_bar(title_bar)
.style(if is_focused {
@@ -403,24 +430,29 @@ impl std::fmt::Display for SelectableWindow {
#[derive(Debug)]
struct Pane {
id: usize,
+ pub scrollable_id: scrollable::Id,
pub axis: pane_grid::Axis,
pub is_pinned: bool,
pub is_moving: bool,
+ pub snapped: bool,
}
impl Pane {
fn new(id: usize, axis: pane_grid::Axis) -> Self {
Self {
id,
+ scrollable_id: scrollable::Id::new(format!("{:?}", id)),
axis,
is_pinned: false,
is_moving: false,
+ snapped: false,
}
}
}
fn view_content<'a>(
pane: pane_grid::Pane,
+ scrollable_id: scrollable::Id,
total_panes: usize,
is_pinned: bool,
size: Size,
@@ -445,7 +477,8 @@ fn view_content<'a>(
button(
"Split vertically",
WindowMessage::Split(pane_grid::Axis::Vertical, pane),
- )
+ ),
+ button("Snap", WindowMessage::SnapToggle,)
]
.spacing(5)
.max_width(150);
@@ -462,15 +495,22 @@ fn view_content<'a>(
controls,
]
.width(Length::Fill)
+ .height(Length::Units(800))
.spacing(10)
.align_items(Alignment::Center);
- container(scrollable(content))
+ Element::from(
+ container(
+ scrollable(content)
+ .vertical_scroll(Properties::new())
+ .id(scrollable_id),
+ )
.width(Length::Fill)
.height(Length::Fill)
.padding(5)
- .center_y()
- .into()
+ .center_y(),
+ )
+ .explain(Color::default())
}
fn view_controls<'a>(
diff --git a/glutin/src/multi_window.rs b/glutin/src/multi_window.rs
index a2e0581a..33fe60ff 100644
--- a/glutin/src/multi_window.rs
+++ b/glutin/src/multi_window.rs
@@ -34,6 +34,7 @@ use std::num::NonZeroU32;
#[cfg(feature = "tracing")]
use tracing::{info_span, instrument::Instrument};
+use iced_native::widget::operation;
#[allow(unsafe_code)]
const ONE: NonZeroU32 = unsafe { NonZeroU32::new_unchecked(1) };
@@ -294,7 +295,7 @@ where
);
#[cfg(feature = "tracing")]
- let run_instance =
+ let run_instance =
run_instance.instrument(info_span!("Application", "LOOP"));
run_instance
@@ -380,7 +381,7 @@ async fn run_instance<A, E, C>(
let mut clipboard =
Clipboard::connect(windows.values().next().expect("No window found"));
- let mut cache = user_interface::Cache::default();
+ let mut caches = HashMap::new();
let mut current_context_window = None;
let mut window_ids: HashMap<_, _> = windows
.iter()
@@ -422,23 +423,20 @@ async fn run_instance<A, E, C>(
let _ = interfaces.insert(id, user_interface);
}
- {
- let state = states.values().next().expect("No state found.");
+ run_command(
+ &application,
+ &mut caches,
+ &states,
+ &mut renderer,
+ init_command,
+ &mut runtime,
+ &mut clipboard,
+ &mut proxy,
+ &mut debug,
+ &windows,
+ || compositor.fetch_information(),
+ );
- run_command(
- &application,
- &mut cache,
- state,
- &mut renderer,
- init_command,
- &mut runtime,
- &mut clipboard,
- &mut proxy,
- &mut debug,
- &windows,
- || compositor.fetch_information(),
- );
- }
runtime.track(application.subscription().map(Event::Application));
let mut mouse_interaction = mouse::Interaction::default();
@@ -501,8 +499,7 @@ async fn run_instance<A, E, C>(
user_interface::State::Outdated
)
{
- let state = &mut states.get_mut(&id).unwrap();
- let pure_states: HashMap<_, _> =
+ let user_interfaces: HashMap<_, _> =
ManuallyDrop::into_inner(interfaces)
.drain()
.map(|(id, interface)| {
@@ -513,8 +510,8 @@ async fn run_instance<A, E, C>(
// Update application
update(
&mut application,
- &mut cache,
- state,
+ &mut caches,
+ &states,
&mut renderer,
&mut runtime,
&mut clipboard,
@@ -526,7 +523,7 @@ async fn run_instance<A, E, C>(
);
// Update window
- state.synchronize(
+ states.get_mut(&id).unwrap().synchronize(
&application,
id,
windows.get(&id).expect("No window found with ID."),
@@ -539,7 +536,7 @@ async fn run_instance<A, E, C>(
&mut renderer,
&mut debug,
&states,
- pure_states,
+ user_interfaces,
));
if should_exit {
@@ -590,7 +587,8 @@ async fn run_instance<A, E, C>(
event::Event::UserEvent(event) => match event {
Event::Application(message) => messages.push(message),
Event::WindowCreated(id, window) => {
- let state = multi_window::State::new(&application, id, &window);
+ let state =
+ multi_window::State::new(&application, id, &window);
let user_interface = multi_window::build_user_interface(
&application,
user_interface::Cache::default(),
@@ -768,7 +766,10 @@ async fn run_instance<A, E, C>(
));
}
} else {
- log::error!("Window state not found for id: {:?}", window_id);
+ log::error!(
+ "Window state not found for id: {:?}",
+ window_id
+ );
}
} else {
log::error!("Window not found for id: {:?}", window_id);
@@ -786,8 +787,8 @@ async fn run_instance<A, E, C>(
/// resulting [`Command`], and tracking its [`Subscription`].
pub fn update<A: Application, E: Executor>(
application: &mut A,
- cache: &mut user_interface::Cache,
- state: &multi_window::State<A>,
+ caches: &mut HashMap<window::Id, user_interface::Cache>,
+ states: &HashMap<window::Id, multi_window::State<A>>,
renderer: &mut A::Renderer,
runtime: &mut Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>,
clipboard: &mut Clipboard,
@@ -797,6 +798,7 @@ pub fn update<A: Application, E: Executor>(
windows: &HashMap<window::Id, winit::window::Window>,
graphics_info: impl FnOnce() -> iced_graphics::compositor::Information + Copy,
) where
+ A: Application + 'static,
<A::Renderer as crate::Renderer>::Theme: StyleSheet,
{
for message in messages.drain(..) {
@@ -808,8 +810,8 @@ pub fn update<A: Application, E: Executor>(
run_command(
application,
- cache,
- state,
+ caches,
+ &states,
renderer,
command,
runtime,
@@ -828,8 +830,8 @@ pub fn update<A: Application, E: Executor>(
/// Runs the actions of a [`Command`].
pub fn run_command<A, E>(
application: &A,
- cache: &mut user_interface::Cache,
- state: &multi_window::State<A>,
+ caches: &mut HashMap<window::Id, user_interface::Cache>,
+ states: &HashMap<window::Id, multi_window::State<A>>,
renderer: &mut A::Renderer,
command: Command<A::Message>,
runtime: &mut Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>,
@@ -839,7 +841,7 @@ pub fn run_command<A, E>(
windows: &HashMap<window::Id, winit::window::Window>,
_graphics_info: impl FnOnce() -> iced_graphics::compositor::Information + Copy,
) where
- A: Application,
+ A: Application + 'static,
E: Executor,
<A::Renderer as crate::Renderer>::Theme: StyleSheet,
{
@@ -967,38 +969,42 @@ pub fn run_command<A, E>(
}
},
command::Action::Widget(action) => {
- use crate::widget::operation;
-
- let mut current_cache = std::mem::take(cache);
+ let mut current_caches = std::mem::take(caches);
let mut current_operation = Some(action.into_operation());
- let mut user_interface = multi_window::build_user_interface(
+ let mut user_interfaces = multi_window::build_user_interfaces(
application,
- current_cache,
renderer,
- state.logical_size(),
debug,
- window::Id::MAIN, // TODO(derezzedex): run the operation on every widget tree
+ states,
+ current_caches,
);
while let Some(mut operation) = current_operation.take() {
- user_interface.operate(renderer, operation.as_mut());
-
- match operation.finish() {
- operation::Outcome::None => {}
- operation::Outcome::Some(message) => {
- proxy
- .send_event(Event::Application(message))
- .expect("Send message to event loop");
- }
- operation::Outcome::Chain(next) => {
- current_operation = Some(next);
+ for user_interface in user_interfaces.values_mut() {
+ user_interface.operate(renderer, operation.as_mut());
+
+ match operation.finish() {
+ operation::Outcome::None => {}
+ operation::Outcome::Some(message) => {
+ proxy
+ .send_event(Event::Application(message))
+ .expect("Send message to event loop");
+ }
+ operation::Outcome::Chain(next) => {
+ current_operation = Some(next);
+ }
}
}
}
- current_cache = user_interface.into_cache();
- *cache = current_cache;
+ let user_interfaces: HashMap<_, _> = user_interfaces
+ .drain()
+ .map(|(id, interface)| (id, interface.into_cache()))
+ .collect();
+
+ current_caches = user_interfaces;
+ *caches = current_caches;
}
}
}
@@ -1010,7 +1016,7 @@ pub fn build_user_interfaces<'a, A>(
renderer: &mut A::Renderer,
debug: &mut Debug,
states: &HashMap<window::Id, multi_window::State<A>>,
- mut pure_states: HashMap<window::Id, user_interface::Cache>,
+ mut user_interfaces: HashMap<window::Id, user_interface::Cache>,
) -> HashMap<
window::Id,
iced_winit::UserInterface<
@@ -1025,7 +1031,7 @@ where
{
let mut interfaces = HashMap::new();
- for (id, pure_state) in pure_states.drain() {
+ for (id, pure_state) in user_interfaces.drain() {
let state = &states.get(&id).unwrap();
let user_interface = multi_window::build_user_interface(
diff --git a/native/src/window.rs b/native/src/window.rs
index aa11756f..e768ed6d 100644
--- a/native/src/window.rs
+++ b/native/src/window.rs
@@ -21,6 +21,7 @@ pub use user_attention::UserAttention;
use crate::subscription::{self, Subscription};
use crate::time::Instant;
+use crate::window;
/// Subscribes to the frames of the window of the running application.
///
@@ -42,6 +43,8 @@ pub fn frames() -> Subscription<Frame> {
/// The returned `Frame` for a framerate subscription.
#[derive(Debug)]
pub struct Frame {
+ /// The `window::Id` that the `Frame` was produced in.
pub id: Id,
+ /// The `Instant` at which the frame was produced.
pub at: Instant,
}
diff --git a/winit/src/multi_window.rs b/winit/src/multi_window.rs
index 430e6706..bd7e9d44 100644
--- a/winit/src/multi_window.rs
+++ b/winit/src/multi_window.rs
@@ -4,12 +4,12 @@ mod state;
pub use state::State;
use crate::clipboard::{self, Clipboard};
-use crate::conversion;
use crate::mouse;
use crate::renderer;
use crate::settings;
use crate::widget::operation;
use crate::window;
+use crate::{conversion, multi_window};
use crate::{
Command, Debug, Element, Error, Executor, Proxy, Renderer, Runtime,
Settings, Size, Subscription,
@@ -254,7 +254,7 @@ where
);
#[cfg(feature = "trace")]
- let run_instance =
+ let run_instance =
run_instance.instrument(info_span!("Application", "LOOP"));
run_instance
@@ -335,7 +335,7 @@ async fn run_instance<A, E, C>(
let mut clipboard =
Clipboard::connect(windows.values().next().expect("No window found"));
- let mut cache = user_interface::Cache::default();
+ let mut caches = HashMap::new();
let mut window_ids: HashMap<_, _> = windows
.iter()
.map(|(&id, window)| (window.id(), id))
@@ -368,26 +368,23 @@ async fn run_instance<A, E, C>(
let _ = states.insert(id, state);
let _ = surfaces.insert(id, surface);
let _ = interfaces.insert(id, user_interface);
+ let _ = caches.insert(id, user_interface::Cache::default());
}
- {
- // TODO(derezzedex)
- let state = states.values().next().expect("No state found");
+ run_command(
+ &application,
+ &mut caches,
+ &states,
+ &mut renderer,
+ init_command,
+ &mut runtime,
+ &mut clipboard,
+ &mut proxy,
+ &mut debug,
+ &windows,
+ || compositor.fetch_information(),
+ );
- run_command(
- &application,
- &mut cache,
- state,
- &mut renderer,
- init_command,
- &mut runtime,
- &mut clipboard,
- &mut proxy,
- &mut debug,
- &windows,
- || compositor.fetch_information(),
- );
- }
runtime.track(application.subscription().map(Event::Application));
let mut mouse_interaction = mouse::Interaction::default();
@@ -455,8 +452,7 @@ async fn run_instance<A, E, C>(
user_interface::State::Outdated,
)
{
- let state = states.get_mut(&id).unwrap();
- let pure_states: HashMap<_, _> =
+ let user_interfaces: HashMap<_, _> =
ManuallyDrop::into_inner(interfaces)
.drain()
.map(
@@ -472,8 +468,8 @@ async fn run_instance<A, E, C>(
// Update application
update(
&mut application,
- &mut cache,
- state,
+ &mut caches,
+ &states,
&mut renderer,
&mut runtime,
&mut clipboard,
@@ -485,7 +481,7 @@ async fn run_instance<A, E, C>(
);
// Update window
- state.synchronize(
+ states.get_mut(&id).unwrap().synchronize(
&application,
id,
windows.get(&id).expect("No window found with ID."),
@@ -498,7 +494,7 @@ async fn run_instance<A, E, C>(
&mut renderer,
&mut debug,
&states,
- pure_states,
+ user_interfaces,
));
if should_exit {
@@ -579,6 +575,7 @@ async fn run_instance<A, E, C>(
let _ = interfaces.insert(id, user_interface);
let _ = window_ids.insert(window.id(), id);
let _ = windows.insert(id, window);
+ let _ = caches.insert(id, user_interface::Cache::default());
}
Event::CloseWindow(id) => {
if let Some(window) = windows.get(&id) {
@@ -764,7 +761,10 @@ async fn run_instance<A, E, C>(
));
}
} else {
- log::error!("No window state found for id: {:?}", window_id);
+ log::error!(
+ "No window state found for id: {:?}",
+ window_id
+ );
}
} else {
log::error!("No window found with id: {:?}", window_id);
@@ -840,8 +840,8 @@ where
/// resulting [`Command`], and tracking its [`Subscription`].
pub fn update<A: Application, E: Executor>(
application: &mut A,
- cache: &mut user_interface::Cache,
- state: &State<A>,
+ caches: &mut HashMap<window::Id, user_interface::Cache>,
+ states: &HashMap<window::Id, State<A>>,
renderer: &mut A::Renderer,
runtime: &mut Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>,
clipboard: &mut Clipboard,
@@ -851,6 +851,7 @@ pub fn update<A: Application, E: Executor>(
windows: &HashMap<window::Id, winit::window::Window>,
graphics_info: impl FnOnce() -> compositor::Information + Copy,
) where
+ A: Application + 'static,
<A::Renderer as crate::Renderer>::Theme: StyleSheet,
{
for message in messages.drain(..) {
@@ -867,8 +868,8 @@ pub fn update<A: Application, E: Executor>(
run_command(
application,
- cache,
- state,
+ caches,
+ states,
renderer,
command,
runtime,
@@ -887,8 +888,8 @@ pub fn update<A: Application, E: Executor>(
/// Runs the actions of a [`Command`].
pub fn run_command<A, E>(
application: &A,
- cache: &mut user_interface::Cache,
- state: &State<A>,
+ caches: &mut HashMap<window::Id, user_interface::Cache>,
+ states: &HashMap<window::Id, State<A>>,
renderer: &mut A::Renderer,
command: Command<A::Message>,
runtime: &mut Runtime<E, Proxy<Event<A::Message>>, Event<A::Message>>,
@@ -898,7 +899,7 @@ pub fn run_command<A, E>(
windows: &HashMap<window::Id, winit::window::Window>,
_graphics_info: impl FnOnce() -> compositor::Information + Copy,
) where
- A: Application,
+ A: Application + 'static,
E: Executor,
<A::Renderer as crate::Renderer>::Theme: StyleSheet,
{
@@ -1024,36 +1025,42 @@ pub fn run_command<A, E>(
}
},
command::Action::Widget(action) => {
- let mut current_cache = std::mem::take(cache);
+ let mut current_caches = std::mem::take(caches);
let mut current_operation = Some(action.into_operation());
- let mut user_interface = build_user_interface(
+ let mut user_interfaces = build_user_interfaces(
application,
- current_cache,
renderer,
- state.logical_size(),
debug,
- window::Id::MAIN, // TODO(derezzedex): run the operation on every widget tree
+ states,
+ current_caches,
);
while let Some(mut operation) = current_operation.take() {
- user_interface.operate(renderer, operation.as_mut());
-
- match operation.finish() {
- operation::Outcome::None => {}
- operation::Outcome::Some(message) => {
- proxy
- .send_event(Event::Application(message))
- .expect("Send message to event loop");
- }
- operation::Outcome::Chain(next) => {
- current_operation = Some(next);
+ for user_interface in user_interfaces.values_mut() {
+ user_interface.operate(renderer, operation.as_mut());
+
+ match operation.finish() {
+ operation::Outcome::None => {}
+ operation::Outcome::Some(message) => {
+ proxy
+ .send_event(Event::Application(message))
+ .expect("Send message to event loop");
+ }
+ operation::Outcome::Chain(next) => {
+ current_operation = Some(next);
+ }
}
}
}
- current_cache = user_interface.into_cache();
- *cache = current_cache;
+ let user_interfaces: HashMap<_, _> = user_interfaces
+ .drain()
+ .map(|(id, interface)| (id, interface.into_cache()))
+ .collect();
+
+ current_caches = user_interfaces;
+ *caches = current_caches;
}
}
}
@@ -1065,7 +1072,7 @@ pub fn build_user_interfaces<'a, A>(
renderer: &mut A::Renderer,
debug: &mut Debug,
states: &HashMap<window::Id, State<A>>,
- mut pure_states: HashMap<window::Id, user_interface::Cache>,
+ mut cached_user_interfaces: HashMap<window::Id, user_interface::Cache>,
) -> HashMap<
window::Id,
UserInterface<
@@ -1080,12 +1087,12 @@ where
{
let mut interfaces = HashMap::new();
- for (id, pure_state) in pure_states.drain() {
+ for (id, cache) in cached_user_interfaces.drain() {
let state = &states.get(&id).unwrap();
let user_interface = build_user_interface(
application,
- pure_state,
+ cache,
renderer,
state.logical_size(),
debug,