Implemented window title update functionality for multiwindow.

This commit is contained in:
bungoboingo 2023-01-05 15:26:28 -08:00
parent 1944e98f82
commit ec41918ec4
14 changed files with 270 additions and 243 deletions

View file

@ -26,6 +26,7 @@ struct Example {
_focused: window::Id, _focused: window::Id,
} }
#[derive(Debug)]
struct Window { struct Window {
title: String, title: String,
panes: pane_grid::State<Pane>, panes: pane_grid::State<Pane>,
@ -80,8 +81,11 @@ impl Application for Example {
) )
} }
fn title(&self) -> String { fn title(&self, window: window::Id) -> String {
String::from("Multi windowed pane grid - Iced") self.windows
.get(&window)
.map(|w| w.title.clone())
.unwrap_or(String::from("New Window"))
} }
fn update(&mut self, message: Message) -> Command<Message> { fn update(&mut self, message: Message) -> Command<Message> {
@ -262,7 +266,6 @@ impl Application for Example {
&window.title, &window.title,
WindowMessage::TitleChanged, WindowMessage::TitleChanged,
), ),
button(text("Apply")).style(theme::Button::Primary),
button(text("Close")) button(text("Close"))
.on_press(WindowMessage::CloseWindow) .on_press(WindowMessage::CloseWindow)
.style(theme::Button::Destructive), .style(theme::Button::Destructive),
@ -389,6 +392,7 @@ impl std::fmt::Display for SelectableWindow {
} }
} }
#[derive(Debug)]
struct Pane { struct Pane {
id: usize, id: usize,
pub axis: pane_grid::Axis, pub axis: pane_grid::Axis,

View file

@ -245,18 +245,7 @@ where
) )
})?; })?;
let (width, height) = window.inner_size().into(); let surface = gl_surface(&display, configuration.as_ref(), &window)
let surface_attributes =
SurfaceAttributesBuilder::<WindowSurface>::new()
.with_srgb(Some(true))
.build(
window_handle,
NonZeroU32::new(width).unwrap_or(ONE),
NonZeroU32::new(height).unwrap_or(ONE),
);
let surface = display
.create_window_surface(configuration.as_ref(), &surface_attributes)
.map_err(|error| { .map_err(|error| {
Error::GraphicsCreationFailed( Error::GraphicsCreationFailed(
iced_graphics::Error::BackendError(format!( iced_graphics::Error::BackendError(format!(
@ -616,3 +605,23 @@ async fn run_instance<A, E, C>(
// Manually drop the user interface // Manually drop the user interface
drop(ManuallyDrop::into_inner(user_interface)); drop(ManuallyDrop::into_inner(user_interface));
} }
#[allow(unsafe_code)]
/// Creates a new [`glutin::Surface<WindowSurface>`].
pub fn gl_surface(
display: &Display,
gl_config: &Config,
window: &winit::window::Window,
) -> Result<Surface<WindowSurface>, glutin::error::Error> {
let (width, height) = window.inner_size().into();
let surface_attributes = SurfaceAttributesBuilder::<WindowSurface>::new()
.with_srgb(Some(true))
.build(
window.raw_window_handle(),
NonZeroU32::new(width).unwrap_or(ONE),
NonZeroU32::new(height).unwrap_or(ONE),
);
unsafe { display.create_window_surface(gl_config, &surface_attributes) }
}

View file

@ -12,7 +12,6 @@ use iced_winit::conversion;
use iced_winit::futures; use iced_winit::futures;
use iced_winit::futures::channel::mpsc; use iced_winit::futures::channel::mpsc;
use iced_winit::renderer; use iced_winit::renderer;
use iced_winit::settings;
use iced_winit::user_interface; use iced_winit::user_interface;
use iced_winit::window; use iced_winit::window;
use iced_winit::winit; use iced_winit::winit;
@ -26,11 +25,12 @@ use glutin::context::{
NotCurrentGlContextSurfaceAccessor, PossiblyCurrentGlContext, NotCurrentGlContextSurfaceAccessor, PossiblyCurrentGlContext,
}; };
use glutin::display::{Display, DisplayApiPreference, GlDisplay}; use glutin::display::{Display, DisplayApiPreference, GlDisplay};
use glutin::surface::{ use glutin::surface::{GlSurface, SwapInterval};
GlSurface, Surface, SurfaceAttributesBuilder, SwapInterval, WindowSurface,
};
use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle}; use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
use crate::application::gl_surface;
use iced_native::window::Action;
use iced_winit::multi_window::Event;
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::CString; use std::ffi::CString;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
@ -76,7 +76,7 @@ where
}; };
let builder = settings.window.into_builder( let builder = settings.window.into_builder(
&application.title(), &application.title(window::Id::MAIN),
event_loop.primary_monitor(), event_loop.primary_monitor(),
settings.id, settings.id,
); );
@ -239,7 +239,14 @@ where
) )
})?; })?;
let surface = gl_surface(&display, configuration.as_ref(), &window); let surface = gl_surface(&display, configuration.as_ref(), &window)
.map_err(|error| {
Error::GraphicsCreationFailed(
iced_graphics::Error::BackendError(format!(
"failed to create surface: {error}"
)),
)
})?;
(display, window, configuration.0, surface, context) (display, window, configuration.0, surface, context)
}; };
@ -301,14 +308,13 @@ where
event: winit::event::WindowEvent::Resized(*new_inner_size), event: winit::event::WindowEvent::Resized(*new_inner_size),
window_id, window_id,
}), }),
winit::event::Event::UserEvent(Event::NewWindow(id, settings)) => { winit::event::Event::UserEvent(Event::NewWindow {
// TODO(derezzedex) id,
settings,
title,
}) => {
let window = settings let window = settings
.into_builder( .into_builder(&title, event_loop.primary_monitor(), None)
"fix window title",
event_loop.primary_monitor(),
None,
)
.build(event_loop) .build(event_loop)
.expect("Failed to build window"); .expect("Failed to build window");
@ -372,9 +378,11 @@ async fn run_instance<A, E, C>(
let mut interfaces = ManuallyDrop::new(HashMap::new()); let mut interfaces = ManuallyDrop::new(HashMap::new());
for (&id, window) in windows.keys().zip(windows.values()) { for (&id, window) in windows.keys().zip(windows.values()) {
let surface = gl_surface(&display, &configuration, &window); let surface = gl_surface(&display, &configuration, &window)
let current_context = context.make_current(&surface).expect("Make current."); .expect("Create surface.");
let state = State::new(&application, &window); let current_context =
context.make_current(&surface).expect("Make current.");
let state = State::new(&application, id, &window);
let physical_size = state.physical_size(); let physical_size = state.physical_size();
surface.resize( surface.resize(
@ -392,7 +400,9 @@ async fn run_instance<A, E, C>(
id, id,
); );
context = current_context.make_not_current().expect("Make not current."); context = current_context
.make_not_current()
.expect("Make not current.");
let _ = states.insert(id, state); let _ = states.insert(id, state);
let _ = surfaces.insert(id, surface); let _ = surfaces.insert(id, surface);
@ -431,7 +441,7 @@ async fn run_instance<A, E, C>(
let (filtered, remaining): (Vec<_>, Vec<_>) = let (filtered, remaining): (Vec<_>, Vec<_>) =
events.iter().cloned().partition( events.iter().cloned().partition(
|(window_id, _event): &( |(window_id, _event): &(
Option<crate::window::Id>, Option<window::Id>,
iced_native::event::Event, iced_native::event::Event,
)| { )| {
*window_id == Some(id) || *window_id == None *window_id == Some(id) || *window_id == None
@ -503,7 +513,11 @@ async fn run_instance<A, E, C>(
); );
// Update window // Update window
state.synchronize(&application, &windows); state.synchronize(
&application,
id,
windows.get(&id).expect("No window found with ID."),
);
let should_exit = application.should_exit(); let should_exit = application.should_exit();
@ -563,7 +577,7 @@ async fn run_instance<A, E, C>(
event::Event::UserEvent(event) => match event { event::Event::UserEvent(event) => match event {
Event::Application(message) => messages.push(message), Event::Application(message) => messages.push(message),
Event::WindowCreated(id, window) => { Event::WindowCreated(id, window) => {
let state = State::new(&application, &window); let state = State::new(&application, id, &window);
let user_interface = multi_window::build_user_interface( let user_interface = multi_window::build_user_interface(
&application, &application,
user_interface::Cache::default(), user_interface::Cache::default(),
@ -573,26 +587,8 @@ async fn run_instance<A, E, C>(
id, id,
); );
let window_handle = window.raw_window_handle(); let surface = gl_surface(&display, &configuration, &window)
let (width, height) = window.inner_size().into(); .expect("Create surface.");
let surface_attributes =
SurfaceAttributesBuilder::<WindowSurface>::new()
.with_srgb(Some(true))
.build(
window_handle,
NonZeroU32::new(width).unwrap_or(ONE),
NonZeroU32::new(height).unwrap_or(ONE),
);
#[allow(unsafe_code)]
let surface = unsafe {
display
.create_window_surface(
&configuration,
&surface_attributes,
)
.expect("failed to create surface")
};
let _ = states.insert(id, state); let _ = states.insert(id, state);
let _ = interfaces.insert(id, user_interface); let _ = interfaces.insert(id, user_interface);
@ -624,7 +620,7 @@ async fn run_instance<A, E, C>(
break 'main; break 'main;
} }
} }
Event::NewWindow(_, _) => unreachable!(), Event::NewWindow { .. } => unreachable!(),
}, },
event::Event::RedrawRequested(id) => { event::Event::RedrawRequested(id) => {
let state = window_ids let state = window_ids
@ -687,9 +683,10 @@ async fn run_instance<A, E, C>(
NonZeroU32::new(physical_size.height).unwrap_or(ONE), NonZeroU32::new(physical_size.height).unwrap_or(ONE),
); );
if let Err(error) = if let Err(_) = surface.set_swap_interval(
surface.set_swap_interval(&current_context, SwapInterval::Wait(ONE)) &current_context,
{ SwapInterval::Wait(ONE),
) {
log::error!("Could not set swap interval for surface attached to window id: {:?}", id); log::error!("Could not set swap interval for surface attached to window id: {:?}", id);
} }
@ -706,9 +703,13 @@ async fn run_instance<A, E, C>(
&debug.overlay(), &debug.overlay(),
); );
surface.swap_buffers(&current_context).expect("Swap buffers"); surface
.swap_buffers(&current_context)
.expect("Swap buffers");
context = current_context.make_not_current().expect("Make not current."); context = current_context
.make_not_current()
.expect("Make not current.");
debug.render_finished(); debug.render_finished();
// TODO: Handle animations! // TODO: Handle animations!
// Maybe we can use `ControlFlow::WaitUntil` for this. // Maybe we can use `ControlFlow::WaitUntil` for this.
@ -751,11 +752,10 @@ async fn run_instance<A, E, C>(
)); ));
} }
} else { } else {
// TODO(derezzedex): log error log::error!("Window state not found for id: {:?}", window_id);
} }
} else { } else {
// TODO(derezzedex): log error log::error!("Window not found for id: {:?}", window_id);
// println!("{:?}: {:?}", window_id, window_event);
} }
} }
_ => {} _ => {}
@ -766,25 +766,6 @@ async fn run_instance<A, E, C>(
// drop(ManuallyDrop::into_inner(user_interface)); // drop(ManuallyDrop::into_inner(user_interface));
} }
/// TODO(derezzedex):
// This is the an wrapper around the `Application::Message` associate type
// to allows the `shell` to create internal messages, while still having
// the current user specified custom messages.
#[derive(Debug)]
pub enum Event<Message> {
/// An [`Application`] generated message
Application(Message),
/// TODO(derezzedex)
// Create a wrapper variant of `window::Event` type instead
// (maybe we should also allow users to listen/react to those internal messages?)
NewWindow(window::Id, settings::Window),
/// TODO(derezzedex)
CloseWindow(window::Id),
/// TODO(derezzedex)
WindowCreated(window::Id, winit::window::Window),
}
/// Updates an [`Application`] by feeding it the provided messages, spawning any /// Updates an [`Application`] by feeding it the provided messages, spawning any
/// resulting [`Command`], and tracking its [`Subscription`]. /// resulting [`Command`], and tracking its [`Subscription`].
pub fn update<A: Application, E: Executor>( pub fn update<A: Application, E: Executor>(
@ -872,7 +853,11 @@ pub fn run_command<A, E>(
command::Action::Window(id, action) => match action { command::Action::Window(id, action) => match action {
window::Action::Spawn { settings } => { window::Action::Spawn { settings } => {
proxy proxy
.send_event(Event::NewWindow(id, settings.into())) .send_event(Event::NewWindow {
id,
settings: settings.into(),
title: application.title(id),
})
.expect("Send message to event loop"); .expect("Send message to event loop");
} }
window::Action::Close => { window::Action::Close => {
@ -934,6 +919,16 @@ pub fn run_command<A, E>(
let window = windows.get(&id).expect("No window found!"); let window = windows.get(&id).expect("No window found!");
window.set_decorations(!window.is_decorated()); window.set_decorations(!window.is_decorated());
} }
Action::RequestUserAttention(attention_type) => {
let window = windows.get(&id).expect("No window found!");
window.request_user_attention(
attention_type.map(conversion::user_attention),
);
}
Action::GainFocus => {
let window = windows.get(&id).expect("No window found!");
window.focus_window();
}
}, },
command::Action::System(action) => match action { command::Action::System(action) => match action {
system::Action::QueryInformation(_tag) => { system::Action::QueryInformation(_tag) => {
@ -1031,26 +1026,3 @@ where
interfaces interfaces
} }
#[allow(unsafe_code)]
fn gl_surface(
display: &Display,
gl_config: &Config,
window: &winit::window::Window,
) -> Surface<WindowSurface> {
let (width, height) = window.inner_size().into();
let surface_attributes = SurfaceAttributesBuilder::<WindowSurface>::new()
.with_srgb(Some(true))
.build(
window.raw_window_handle(),
NonZeroU32::new(width).unwrap_or(ONE),
NonZeroU32::new(height).unwrap_or(ONE),
);
unsafe {
display
.create_window_surface(gl_config, &surface_attributes)
.expect("failed to create surface")
}
}

View file

@ -8,7 +8,6 @@ use iced_winit::winit;
use winit::event::{Touch, WindowEvent}; use winit::event::{Touch, WindowEvent};
use winit::window::Window; use winit::window::Window;
use std::collections::HashMap;
use std::marker::PhantomData; use std::marker::PhantomData;
/// The state of a windowed [`Application`]. /// The state of a windowed [`Application`].
@ -33,8 +32,8 @@ where
<A::Renderer as crate::Renderer>::Theme: application::StyleSheet, <A::Renderer as crate::Renderer>::Theme: application::StyleSheet,
{ {
/// Creates a new [`State`] for the provided [`Application`] and window. /// Creates a new [`State`] for the provided [`Application`] and window.
pub fn new(application: &A, window: &Window) -> Self { pub fn new(application: &A, window_id: window::Id, window: &Window) -> Self {
let title = application.title(); let title = application.title(window_id);
let scale_factor = application.scale_factor(); let scale_factor = application.scale_factor();
let theme = application.theme(); let theme = application.theme();
let appearance = theme.appearance(&application.style()); let appearance = theme.appearance(&application.style());
@ -67,7 +66,7 @@ where
&self.viewport &self.viewport
} }
/// TODO(derezzedex) /// Returns whether or not the current [`Viewport`] has changed.
pub fn viewport_changed(&self) -> bool { pub fn viewport_changed(&self) -> bool {
self.viewport_changed self.viewport_changed
} }
@ -187,12 +186,11 @@ where
pub fn synchronize( pub fn synchronize(
&mut self, &mut self,
application: &A, application: &A,
windows: &HashMap<window::Id, Window>, window_id: window::Id,
window: &Window,
) { ) {
let window = windows.values().next().expect("No window found");
// Update window title // Update window title
let new_title = application.title(); let new_title = application.title(window_id);
if self.title != new_title { if self.title != new_title {
window.set_title(&new_title); window.set_title(&new_title);

View file

@ -4,6 +4,8 @@ mod event;
mod icon; mod icon;
mod id; mod id;
mod mode; mod mode;
mod position;
mod settings;
mod user_attention; mod user_attention;
pub use action::Action; pub use action::Action;
@ -11,6 +13,6 @@ pub use event::Event;
pub use icon::Icon; pub use icon::Icon;
pub use id::Id; pub use id::Id;
pub use mode::Mode; pub use mode::Mode;
pub use user_attention::UserAttention;
pub use position::Position; pub use position::Position;
pub use settings::Settings; pub use settings::Settings;
pub use user_attention::UserAttention;

View file

@ -1,4 +1,4 @@
use crate::window::{self, Mode, UserAttention}; use crate::window;
use iced_futures::MaybeSend; use iced_futures::MaybeSend;
use std::fmt; use std::fmt;
@ -13,9 +13,9 @@ pub enum Action<T> {
/// Theres no guarantee that this will work unless the left mouse /// Theres no guarantee that this will work unless the left mouse
/// button was pressed immediately before this function is called. /// button was pressed immediately before this function is called.
Drag, Drag,
/// TODO(derezzedex) /// Spawns a new window with the provided [`window::Settings`].
Spawn { Spawn {
/// TODO(derezzedex) /// The settings of the [`Window`].
settings: window::Settings, settings: window::Settings,
}, },
/// Resize the window. /// Resize the window.
@ -62,7 +62,7 @@ pub enum Action<T> {
/// - **macOS:** `None` has no effect. /// - **macOS:** `None` has no effect.
/// - **X11:** Requests for user attention must be manually cleared. /// - **X11:** Requests for user attention must be manually cleared.
/// - **Wayland:** Requires `xdg_activation_v1` protocol, `None` has no effect. /// - **Wayland:** Requires `xdg_activation_v1` protocol, `None` has no effect.
RequestUserAttention(Option<UserAttention>), RequestUserAttention(Option<window::UserAttention>),
/// Brings the window to the front and sets input focus. Has no effect if the window is /// Brings the window to the front and sets input focus. Has no effect if the window is
/// already in focus, minimized, or not visible. /// already in focus, minimized, or not visible.
/// ///

View file

@ -3,10 +3,10 @@
/// The icon of a window. /// The icon of a window.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct Icon { pub struct Icon {
/// TODO(derezzedex) /// The __rgba__ color data of the window [`Icon`].
pub rgba: Vec<u8>, pub rgba: Vec<u8>,
/// TODO(derezzedex) /// The width of the window [`Icon`].
pub width: u32, pub width: u32,
/// TODO(derezzedex) /// The height of the window [`Icon`].
pub height: u32, pub height: u32,
} }

View file

@ -1,15 +1,18 @@
use std::collections::hash_map::DefaultHasher; use std::collections::hash_map::DefaultHasher;
use std::fmt::{Display, Formatter};
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
/// TODO(derezzedex) /// The ID of the window.
///
/// This is not necessarily the same as the window ID fetched from `winit::window::Window`.
pub struct Id(u64); pub struct Id(u64);
impl Id { impl Id {
/// TODO(derezzedex): maybe change `u64` to an enum `Type::{Single, Multi(u64)}` /// TODO(derezzedex): maybe change `u64` to an enum `Type::{Single, Multi(u64)}`
pub const MAIN: Self = Id(0); pub const MAIN: Self = Id(0);
/// TODO(derezzedex) /// Creates a new unique window ID.
pub fn new(id: impl Hash) -> Id { pub fn new(id: impl Hash) -> Id {
let mut hasher = DefaultHasher::new(); let mut hasher = DefaultHasher::new();
id.hash(&mut hasher); id.hash(&mut hasher);
@ -17,3 +20,9 @@ impl Id {
Id(hasher.finish()) Id(hasher.finish())
} }
} }
impl Display for Id {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "Id({})", self.0)
}
}

View file

@ -44,7 +44,7 @@ pub trait Application: Sized {
/// ///
/// This title can be dynamic! The runtime will automatically update the /// This title can be dynamic! The runtime will automatically update the
/// title of your application when necessary. /// title of your application when necessary.
fn title(&self) -> String; fn title(&self, window: window::Id) -> String;
/// Handles a __message__ and updates the state of the [`Application`]. /// Handles a __message__ and updates the state of the [`Application`].
/// ///
@ -110,7 +110,7 @@ pub trait Application: Sized {
false false
} }
/// TODO(derezzedex) /// Requests that the [`window`] be closed.
fn close_requested(&self, window: window::Id) -> Self::Message; fn close_requested(&self, window: window::Id) -> Self::Message;
/// Runs the [`Application`]. /// Runs the [`Application`].
@ -163,8 +163,8 @@ where
(Instance(app), command) (Instance(app), command)
} }
fn title(&self) -> String { fn title(&self, window: window::Id) -> String {
self.0.title() self.0.title(window)
} }
fn update(&mut self, message: Self::Message) -> Command<Self::Message> { fn update(&mut self, message: Self::Message) -> Command<Self::Message> {

View file

@ -675,7 +675,7 @@ pub fn run_command<A, E>(
window::Action::Drag => { window::Action::Drag => {
let _res = window.drag_window(); let _res = window.drag_window();
} }
window::Action::Spawn { .. } | window::Action::Close => { window::Action::Spawn { .. } => {
log::info!( log::info!(
"This is only available on `multi_window::Application`" "This is only available on `multi_window::Application`"
) )

View file

@ -22,6 +22,7 @@ use iced_native::user_interface::{self, UserInterface};
pub use iced_native::application::{Appearance, StyleSheet}; pub use iced_native::application::{Appearance, StyleSheet};
use iced_native::window::Action;
use std::collections::HashMap; use std::collections::HashMap;
use std::mem::ManuallyDrop; use std::mem::ManuallyDrop;
@ -36,7 +37,14 @@ pub enum Event<Message> {
/// TODO(derezzedex) /// TODO(derezzedex)
// Create a wrapper variant of `window::Event` type instead // Create a wrapper variant of `window::Event` type instead
// (maybe we should also allow users to listen/react to those internal messages?) // (maybe we should also allow users to listen/react to those internal messages?)
NewWindow(window::Id, settings::Window), NewWindow {
/// The [window::Id] of the newly spawned [`Window`].
id: window::Id,
/// The [settings::Window] of the newly spawned [`Window`].
settings: settings::Window,
/// The title of the newly spawned [`Window`].
title: String,
},
/// TODO(derezzedex) /// TODO(derezzedex)
CloseWindow(window::Id), CloseWindow(window::Id),
/// TODO(derezzedex) /// TODO(derezzedex)
@ -95,11 +103,11 @@ where
/// load state from a file, perform an initial HTTP request, etc. /// load state from a file, perform an initial HTTP request, etc.
fn new(flags: Self::Flags) -> (Self, Command<Self::Message>); fn new(flags: Self::Flags) -> (Self, Command<Self::Message>);
/// Returns the current title of the [`Application`]. /// Returns the current title of the current [`Application`] window.
/// ///
/// This title can be dynamic! The runtime will automatically update the /// This title can be dynamic! The runtime will automatically update the
/// title of your application when necessary. /// title of your application when necessary.
fn title(&self) -> String; fn title(&self, window_id: window::Id) -> String;
/// Returns the current [`Theme`] of the [`Application`]. /// Returns the current [`Theme`] of the [`Application`].
fn theme(&self) -> <Self::Renderer as crate::Renderer>::Theme; fn theme(&self) -> <Self::Renderer as crate::Renderer>::Theme;
@ -144,7 +152,7 @@ where
false false
} }
/// TODO(derezzedex) /// Requests that the [`window`] be closed.
fn close_requested(&self, window: window::Id) -> Self::Message; fn close_requested(&self, window: window::Id) -> Self::Message;
} }
@ -184,7 +192,7 @@ where
}; };
let builder = settings.window.into_builder( let builder = settings.window.into_builder(
&application.title(), &application.title(window::Id::MAIN),
event_loop.primary_monitor(), event_loop.primary_monitor(),
settings.id, settings.id,
); );
@ -253,14 +261,13 @@ where
event: winit::event::WindowEvent::Resized(*new_inner_size), event: winit::event::WindowEvent::Resized(*new_inner_size),
window_id, window_id,
}), }),
winit::event::Event::UserEvent(Event::NewWindow(id, settings)) => { winit::event::Event::UserEvent(Event::NewWindow {
// TODO(derezzedex) id,
settings,
title,
}) => {
let window = settings let window = settings
.into_builder( .into_builder(&title, event_loop.primary_monitor(), None)
"fix window title",
event_loop.primary_monitor(),
None,
)
.build(event_loop) .build(event_loop)
.expect("Failed to build window"); .expect("Failed to build window");
@ -320,10 +327,7 @@ async fn run_instance<A, E, C>(
for (&id, window) in windows.keys().zip(windows.values()) { for (&id, window) in windows.keys().zip(windows.values()) {
let mut surface = compositor.create_surface(window); let mut surface = compositor.create_surface(window);
println!("Creating surface for window: {:?}", window); let state = State::new(&application, id, window);
let state = State::new(&application, window);
let physical_size = state.physical_size(); let physical_size = state.physical_size();
compositor.configure_surface( compositor.configure_surface(
@ -457,7 +461,11 @@ async fn run_instance<A, E, C>(
); );
// Update window // Update window
state.synchronize(&application, &windows); state.synchronize(
&application,
id,
windows.get(&id).expect("No window found with ID."),
);
let should_exit = application.should_exit(); let should_exit = application.should_exit();
@ -516,15 +524,14 @@ async fn run_instance<A, E, C>(
), ),
)); ));
} }
event::Event::UserEvent(event) => { event::Event::UserEvent(event) => match event {
match event {
Event::Application(message) => { Event::Application(message) => {
messages.push(message); messages.push(message);
} }
Event::WindowCreated(id, window) => { Event::WindowCreated(id, window) => {
let mut surface = compositor.create_surface(&window); let mut surface = compositor.create_surface(&window);
let state = State::new(&application, &window); let state = State::new(&application, id, &window);
let physical_size = state.physical_size(); let physical_size = state.physical_size();
@ -550,38 +557,52 @@ async fn run_instance<A, E, C>(
let _ = windows.insert(id, window); let _ = windows.insert(id, window);
} }
Event::CloseWindow(id) => { Event::CloseWindow(id) => {
println!("Closing window {:?}. Total: {}", id, windows.len());
if let Some(window) = windows.get(&id) { if let Some(window) = windows.get(&id) {
if window_ids.remove(&window.id()).is_none() { if window_ids.remove(&window.id()).is_none() {
log::error!("Failed to remove window with id {:?} from window_ids.", window.id()); log::error!("Failed to remove window with id {:?} from window_ids.", window.id());
} }
} else { } else {
log::error!("Could not find window with id {:?} in windows.", id); log::error!(
"Could not find window with id {:?} in windows.",
id
);
} }
if states.remove(&id).is_none() { if states.remove(&id).is_none() {
log::error!("Failed to remove window {:?} from states.", id); log::error!(
"Failed to remove window {:?} from states.",
id
);
} }
if interfaces.remove(&id).is_none() { if interfaces.remove(&id).is_none() {
log::error!("Failed to remove window {:?} from interfaces.", id); log::error!(
"Failed to remove window {:?} from interfaces.",
id
);
} }
if windows.remove(&id).is_none() { if windows.remove(&id).is_none() {
log::error!("Failed to remove window {:?} from windows.", id); log::error!(
"Failed to remove window {:?} from windows.",
id
);
} }
if surfaces.remove(&id).is_none() { if surfaces.remove(&id).is_none() {
log::error!("Failed to remove window {:?} from surfaces.", id); log::error!(
"Failed to remove window {:?} from surfaces.",
id
);
} }
if windows.is_empty() { if windows.is_empty() {
log::info!("All windows are closed. Terminating program."); log::info!(
"All windows are closed. Terminating program."
);
break 'main; break 'main;
} else { } else {
log::info!("Remaining windows: {:?}", windows.len()); log::info!("Remaining windows: {:?}", windows.len());
} }
} }
Event::NewWindow(_, _) => unreachable!(), Event::NewWindow { .. } => unreachable!(),
} },
}
event::Event::RedrawRequested(id) => { event::Event::RedrawRequested(id) => {
let state = window_ids let state = window_ids
.get(&id) .get(&id)
@ -716,11 +737,10 @@ async fn run_instance<A, E, C>(
)); ));
} }
} else { } else {
// TODO(derezzedex): log error log::error!("No window state found for id: {:?}", window_id);
} }
} else { } else {
// TODO(derezzedex): log error log::error!("No window found with id: {:?}", window_id);
// println!("{:?}: {:?}", window_id, window_event);
} }
} }
_ => {} _ => {}
@ -864,7 +884,11 @@ pub fn run_command<A, E>(
command::Action::Window(id, action) => match action { command::Action::Window(id, action) => match action {
window::Action::Spawn { settings } => { window::Action::Spawn { settings } => {
proxy proxy
.send_event(Event::NewWindow(id, settings.into())) .send_event(Event::NewWindow {
id,
settings: settings.into(),
title: application.title(id),
})
.expect("Send message to event loop"); .expect("Send message to event loop");
} }
window::Action::Close => { window::Action::Close => {
@ -926,6 +950,16 @@ pub fn run_command<A, E>(
let window = windows.get(&id).expect("No window found!"); let window = windows.get(&id).expect("No window found!");
window.set_decorations(!window.is_decorated()); window.set_decorations(!window.is_decorated());
} }
window::Action::RequestUserAttention(attention_type) => {
let window = windows.get(&id).expect("No window found!");
window.request_user_attention(
attention_type.map(conversion::user_attention),
);
}
Action::GainFocus => {
let window = windows.get(&id).expect("No window found!");
window.focus_window();
}
}, },
command::Action::System(action) => match action { command::Action::System(action) => match action {
system::Action::QueryInformation(_tag) => { system::Action::QueryInformation(_tag) => {

View file

@ -4,7 +4,6 @@ use crate::multi_window::Application;
use crate::window; use crate::window;
use crate::{Color, Debug, Point, Size, Viewport}; use crate::{Color, Debug, Point, Size, Viewport};
use std::collections::HashMap;
use std::marker::PhantomData; use std::marker::PhantomData;
use winit::event::{Touch, WindowEvent}; use winit::event::{Touch, WindowEvent};
use winit::window::Window; use winit::window::Window;
@ -31,8 +30,8 @@ where
<A::Renderer as crate::Renderer>::Theme: application::StyleSheet, <A::Renderer as crate::Renderer>::Theme: application::StyleSheet,
{ {
/// Creates a new [`State`] for the provided [`Application`] and window. /// Creates a new [`State`] for the provided [`Application`] and window.
pub fn new(application: &A, window: &Window) -> Self { pub fn new(application: &A, window_id: window::Id, window: &Window) -> Self {
let title = application.title(); let title = application.title(window_id);
let scale_factor = application.scale_factor(); let scale_factor = application.scale_factor();
let theme = application.theme(); let theme = application.theme();
let appearance = theme.appearance(&application.style()); let appearance = theme.appearance(&application.style());
@ -65,7 +64,7 @@ where
&self.viewport &self.viewport
} }
/// TODO(derezzedex) /// Returns whether or not the viewport changed.
pub fn viewport_changed(&self) -> bool { pub fn viewport_changed(&self) -> bool {
self.viewport_changed self.viewport_changed
} }
@ -184,12 +183,11 @@ where
pub fn synchronize( pub fn synchronize(
&mut self, &mut self,
application: &A, application: &A,
windows: &HashMap<window::Id, Window>, window_id: window::Id,
window: &Window,
) { ) {
let window = windows.values().next().expect("No window found");
// Update window title // Update window title
let new_title = application.title(); let new_title = application.title(window_id);
if self.title != new_title { if self.title != new_title {
window.set_title(&new_title); window.set_title(&new_title);

View file

@ -2,19 +2,19 @@
use crate::command::{self, Command}; use crate::command::{self, Command};
use iced_native::window; use iced_native::window;
pub use window::{Id, Event, Mode, UserAttention}; pub use window::{Event, Id, Mode, UserAttention};
/// Closes the current window and exits the application. /// Closes the window.
pub fn close<Message>() -> Command<Message> { pub fn close<Message>(id: window::Id) -> Command<Message> {
Command::single(command::Action::Window(window::Action::Close)) Command::single(command::Action::Window(id, window::Action::Close))
} }
/// Begins dragging the window while the left mouse button is held. /// Begins dragging the window while the left mouse button is held.
pub fn drag<Message>() -> Command<Message> { pub fn drag<Message>(id: window::Id) -> Command<Message> {
Command::single(command::Action::Window(window::Action::Drag)) Command::single(command::Action::Window(id, window::Action::Drag))
} }
/// TODO(derezzedex) /// Spawns a new window.
pub fn spawn<Message>( pub fn spawn<Message>(
id: window::Id, id: window::Id,
settings: window::Settings, settings: window::Settings,
@ -25,11 +25,6 @@ pub fn spawn<Message>(
)) ))
} }
/// TODO(derezzedex)
pub fn close<Message>(id: window::Id) -> Command<Message> {
Command::single(command::Action::Window(id, window::Action::Close))
}
/// Resizes the window to the given logical dimensions. /// Resizes the window to the given logical dimensions.
pub fn resize<Message>( pub fn resize<Message>(
id: window::Id, id: window::Id,
@ -43,13 +38,19 @@ pub fn resize<Message>(
} }
/// Sets the window to maximized or back. /// Sets the window to maximized or back.
pub fn maximize<Message>(value: bool) -> Command<Message> { pub fn maximize<Message>(id: window::Id, value: bool) -> Command<Message> {
Command::single(command::Action::Window(window::Action::Maximize(value))) Command::single(command::Action::Window(
id,
window::Action::Maximize(value),
))
} }
/// Set the window to minimized or back. /// Set the window to minimized or back.
pub fn minimize<Message>(value: bool) -> Command<Message> { pub fn minimize<Message>(id: window::Id, value: bool) -> Command<Message> {
Command::single(command::Action::Window(window::Action::Minimize(value))) Command::single(command::Action::Window(
id,
window::Action::Minimize(value),
))
} }
/// Moves a window to the given logical coordinates. /// Moves a window to the given logical coordinates.
@ -63,8 +64,8 @@ pub fn set_mode<Message>(id: window::Id, mode: Mode) -> Command<Message> {
} }
/// Sets the window to maximized or back. /// Sets the window to maximized or back.
pub fn toggle_maximize<Message>() -> Command<Message> { pub fn toggle_maximize<Message>(id: window::Id) -> Command<Message> {
Command::single(command::Action::Window(window::Action::ToggleMaximize)) Command::single(command::Action::Window(id, window::Action::ToggleMaximize))
} }
/// Fetches the current [`Mode`] of the window. /// Fetches the current [`Mode`] of the window.