Merge pull request #804 from hecrj/feature/graceful-exit
Graceful exiting for `Application`
This commit is contained in:
commit
b9ec44446e
@ -1,22 +1,30 @@
|
||||
use iced::{
|
||||
executor, Align, Application, Checkbox, Clipboard, Column, Command,
|
||||
Container, Element, Length, Settings, Subscription, Text,
|
||||
button, executor, Align, Application, Button, Checkbox, Clipboard, Column,
|
||||
Command, Container, Element, HorizontalAlignment, Length, Settings,
|
||||
Subscription, Text,
|
||||
};
|
||||
use iced_native::{window, Event};
|
||||
|
||||
pub fn main() -> iced::Result {
|
||||
Events::run(Settings::default())
|
||||
Events::run(Settings {
|
||||
exit_on_close_request: false,
|
||||
..Settings::default()
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct Events {
|
||||
last: Vec<iced_native::Event>,
|
||||
enabled: bool,
|
||||
exit: button::State,
|
||||
should_exit: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum Message {
|
||||
EventOccurred(iced_native::Event),
|
||||
Toggled(bool),
|
||||
Exit,
|
||||
}
|
||||
|
||||
impl Application for Events {
|
||||
@ -38,27 +46,35 @@ impl Application for Events {
|
||||
_clipboard: &mut Clipboard,
|
||||
) -> Command<Message> {
|
||||
match message {
|
||||
Message::EventOccurred(event) => {
|
||||
Message::EventOccurred(event) if self.enabled => {
|
||||
self.last.push(event);
|
||||
|
||||
if self.last.len() > 5 {
|
||||
let _ = self.last.remove(0);
|
||||
}
|
||||
}
|
||||
Message::EventOccurred(event) => {
|
||||
if let Event::Window(window::Event::CloseRequested) = event {
|
||||
self.should_exit = true;
|
||||
}
|
||||
}
|
||||
Message::Toggled(enabled) => {
|
||||
self.enabled = enabled;
|
||||
}
|
||||
Message::Exit => {
|
||||
self.should_exit = true;
|
||||
}
|
||||
};
|
||||
|
||||
Command::none()
|
||||
}
|
||||
|
||||
fn subscription(&self) -> Subscription<Message> {
|
||||
if self.enabled {
|
||||
iced_native::subscription::events().map(Message::EventOccurred)
|
||||
} else {
|
||||
Subscription::none()
|
||||
}
|
||||
iced_native::subscription::events().map(Message::EventOccurred)
|
||||
}
|
||||
|
||||
fn should_exit(&self) -> bool {
|
||||
self.should_exit
|
||||
}
|
||||
|
||||
fn view(&mut self) -> Element<Message> {
|
||||
@ -75,11 +91,22 @@ impl Application for Events {
|
||||
Message::Toggled,
|
||||
);
|
||||
|
||||
let exit = Button::new(
|
||||
&mut self.exit,
|
||||
Text::new("Exit")
|
||||
.width(Length::Fill)
|
||||
.horizontal_alignment(HorizontalAlignment::Center),
|
||||
)
|
||||
.width(Length::Units(100))
|
||||
.padding(10)
|
||||
.on_press(Message::Exit);
|
||||
|
||||
let content = Column::new()
|
||||
.align_items(Align::Center)
|
||||
.spacing(20)
|
||||
.push(events)
|
||||
.push(toggle);
|
||||
.push(toggle)
|
||||
.push(exit);
|
||||
|
||||
Container::new(content)
|
||||
.width(Length::Fill)
|
||||
|
@ -92,10 +92,11 @@ where
|
||||
application,
|
||||
compositor,
|
||||
renderer,
|
||||
context,
|
||||
runtime,
|
||||
debug,
|
||||
receiver,
|
||||
context,
|
||||
settings.exit_on_close_request,
|
||||
));
|
||||
|
||||
let mut context = task::Context::from_waker(task::noop_waker_ref());
|
||||
@ -139,10 +140,11 @@ async fn run_instance<A, E, C>(
|
||||
mut application: A,
|
||||
mut compositor: C,
|
||||
mut renderer: A::Renderer,
|
||||
context: glutin::ContextWrapper<glutin::PossiblyCurrent, Window>,
|
||||
mut runtime: Runtime<E, Proxy<A::Message>, A::Message>,
|
||||
mut debug: Debug,
|
||||
mut receiver: mpsc::UnboundedReceiver<glutin::event::Event<'_, A::Message>>,
|
||||
context: glutin::ContextWrapper<glutin::PossiblyCurrent, Window>,
|
||||
exit_on_close_request: bool,
|
||||
) where
|
||||
A: Application + 'static,
|
||||
E: Executor + 'static,
|
||||
@ -212,6 +214,8 @@ async fn run_instance<A, E, C>(
|
||||
// Update window
|
||||
state.synchronize(&application, context.window());
|
||||
|
||||
let should_exit = application.should_exit();
|
||||
|
||||
user_interface =
|
||||
ManuallyDrop::new(application::build_user_interface(
|
||||
&mut application,
|
||||
@ -220,6 +224,10 @@ async fn run_instance<A, E, C>(
|
||||
state.logical_size(),
|
||||
&mut debug,
|
||||
));
|
||||
|
||||
if should_exit {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
debug.draw_started();
|
||||
@ -290,6 +298,7 @@ async fn run_instance<A, E, C>(
|
||||
..
|
||||
} => {
|
||||
if application::requests_exit(&window_event, state.modifiers())
|
||||
&& exit_on_close_request
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
@ -12,6 +12,12 @@ pub enum Event {
|
||||
height: u32,
|
||||
},
|
||||
|
||||
/// The user has requested for the window to close.
|
||||
///
|
||||
/// Usually, you will want to terminate the execution whenever this event
|
||||
/// occurs.
|
||||
CloseRequested,
|
||||
|
||||
/// A window was focused.
|
||||
Focused,
|
||||
|
||||
|
@ -184,6 +184,13 @@ pub trait Application: Sized {
|
||||
1.0
|
||||
}
|
||||
|
||||
/// Returns whether the [`Application`] should be terminated.
|
||||
///
|
||||
/// By default, it returns `false`.
|
||||
fn should_exit(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Runs the [`Application`].
|
||||
///
|
||||
/// On native platforms, this method will take control of the current thread
|
||||
@ -284,6 +291,10 @@ where
|
||||
fn scale_factor(&self) -> f64 {
|
||||
self.0.scale_factor()
|
||||
}
|
||||
|
||||
fn should_exit(&self) -> bool {
|
||||
self.0.should_exit()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
|
@ -25,6 +25,10 @@ pub struct Settings<Flags> {
|
||||
/// The default value is 20.
|
||||
pub default_text_size: u16,
|
||||
|
||||
/// Whether the [`Application`] should exit when the user requests the
|
||||
/// window to close (e.g. the user presses the close button).
|
||||
pub exit_on_close_request: bool,
|
||||
|
||||
/// If set to true, the renderer will try to perform antialiasing for some
|
||||
/// primitives.
|
||||
///
|
||||
@ -46,10 +50,11 @@ impl<Flags> Settings<Flags> {
|
||||
|
||||
Self {
|
||||
flags,
|
||||
antialiasing: default_settings.antialiasing,
|
||||
window: default_settings.window,
|
||||
default_font: default_settings.default_font,
|
||||
default_text_size: default_settings.default_text_size,
|
||||
window: default_settings.window,
|
||||
exit_on_close_request: default_settings.exit_on_close_request,
|
||||
antialiasing: default_settings.antialiasing,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -61,10 +66,11 @@ where
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
flags: Default::default(),
|
||||
antialiasing: Default::default(),
|
||||
window: Default::default(),
|
||||
default_font: Default::default(),
|
||||
default_text_size: 20,
|
||||
window: Default::default(),
|
||||
exit_on_close_request: true,
|
||||
antialiasing: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -75,6 +81,7 @@ impl<Flags> From<Settings<Flags>> for iced_winit::Settings<Flags> {
|
||||
iced_winit::Settings {
|
||||
window: settings.window.into(),
|
||||
flags: settings.flags,
|
||||
exit_on_close_request: settings.exit_on_close_request,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -91,6 +91,13 @@ pub trait Application: Program<Clipboard = Clipboard> {
|
||||
fn scale_factor(&self) -> f64 {
|
||||
1.0
|
||||
}
|
||||
|
||||
/// Returns whether the [`Application`] should be terminated.
|
||||
///
|
||||
/// By default, it returns `false`.
|
||||
fn should_exit(&self) -> bool {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/// Runs an [`Application`] with an executor, compositor, and the provided
|
||||
@ -149,10 +156,11 @@ where
|
||||
application,
|
||||
compositor,
|
||||
renderer,
|
||||
window,
|
||||
runtime,
|
||||
debug,
|
||||
receiver,
|
||||
window,
|
||||
settings.exit_on_close_request,
|
||||
));
|
||||
|
||||
let mut context = task::Context::from_waker(task::noop_waker_ref());
|
||||
@ -196,10 +204,11 @@ async fn run_instance<A, E, C>(
|
||||
mut application: A,
|
||||
mut compositor: C,
|
||||
mut renderer: A::Renderer,
|
||||
window: winit::window::Window,
|
||||
mut runtime: Runtime<E, Proxy<A::Message>, A::Message>,
|
||||
mut debug: Debug,
|
||||
mut receiver: mpsc::UnboundedReceiver<winit::event::Event<'_, A::Message>>,
|
||||
window: winit::window::Window,
|
||||
exit_on_close_request: bool,
|
||||
) where
|
||||
A: Application + 'static,
|
||||
E: Executor + 'static,
|
||||
@ -279,6 +288,8 @@ async fn run_instance<A, E, C>(
|
||||
// Update window
|
||||
state.synchronize(&application, &window);
|
||||
|
||||
let should_exit = application.should_exit();
|
||||
|
||||
user_interface = ManuallyDrop::new(build_user_interface(
|
||||
&mut application,
|
||||
cache,
|
||||
@ -286,6 +297,10 @@ async fn run_instance<A, E, C>(
|
||||
state.logical_size(),
|
||||
&mut debug,
|
||||
));
|
||||
|
||||
if should_exit {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
debug.draw_started();
|
||||
@ -358,7 +373,9 @@ async fn run_instance<A, E, C>(
|
||||
event: window_event,
|
||||
..
|
||||
} => {
|
||||
if requests_exit(&window_event, state.modifiers()) {
|
||||
if requests_exit(&window_event, state.modifiers())
|
||||
&& exit_on_close_request
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,9 @@ pub fn window_event(
|
||||
height: logical_size.height,
|
||||
}))
|
||||
}
|
||||
WindowEvent::CloseRequested => {
|
||||
Some(Event::Window(window::Event::CloseRequested))
|
||||
}
|
||||
WindowEvent::CursorMoved { position, .. } => {
|
||||
let position = position.to_logical::<f64>(scale_factor);
|
||||
|
||||
|
@ -23,6 +23,10 @@ pub struct Settings<Flags> {
|
||||
///
|
||||
/// [`Application`]: crate::Application
|
||||
pub flags: Flags,
|
||||
|
||||
/// Whether the [`Application`] should exit when the user requests the
|
||||
/// window to close (e.g. the user presses the close button).
|
||||
pub exit_on_close_request: bool,
|
||||
}
|
||||
|
||||
/// The window settings of an application.
|
||||
|
Loading…
Reference in New Issue
Block a user