Skip to content

Commit

Permalink
Merge pull request #804 from hecrj/feature/graceful-exit
Browse files Browse the repository at this point in the history
Graceful exiting for `Application`
  • Loading branch information
hecrj authored Mar 31, 2021
2 parents bbb4e46 + 8f95245 commit b9ec444
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 19 deletions.
47 changes: 37 additions & 10 deletions examples/events/src/main.rs
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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> {
Expand All @@ -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)
Expand Down
13 changes: 11 additions & 2 deletions glutin/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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());
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -220,6 +224,10 @@ async fn run_instance<A, E, C>(
state.logical_size(),
&mut debug,
));

if should_exit {
break;
}
}

debug.draw_started();
Expand Down Expand Up @@ -290,6 +298,7 @@ async fn run_instance<A, E, C>(
..
} => {
if application::requests_exit(&window_event, state.modifiers())
&& exit_on_close_request
{
break;
}
Expand Down
6 changes: 6 additions & 0 deletions native/src/window/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand Down
11 changes: 11 additions & 0 deletions src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")]
Expand Down
15 changes: 11 additions & 4 deletions src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
///
Expand All @@ -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,
}
}
}
Expand All @@ -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,
}
}
}
Expand All @@ -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,
}
}
}
23 changes: 20 additions & 3 deletions winit/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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());
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -279,13 +288,19 @@ 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,
&mut renderer,
state.logical_size(),
&mut debug,
));

if should_exit {
break;
}
}

debug.draw_started();
Expand Down Expand Up @@ -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;
}

Expand Down
3 changes: 3 additions & 0 deletions winit/src/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
4 changes: 4 additions & 0 deletions winit/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down

0 comments on commit b9ec444

Please sign in to comment.