Skip to content

Commit 3b3c4e7

Browse files
committed
Terminate app run loop on Windows when all windows have closed.
1 parent d8b6e14 commit 3b3c4e7

20 files changed

+467
-63
lines changed

druid-shell/examples/perftest.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use time::Instant;
1919
use piet_common::kurbo::{Line, Rect};
2020
use piet_common::{Color, FontBuilder, Piet, RenderContext, Text, TextLayoutBuilder};
2121

22-
use druid_shell::{Application, KeyEvent, WinHandler, WindowBuilder, WindowHandle};
22+
use druid_shell::{AppState, Application, KeyEvent, WinHandler, WindowBuilder, WindowHandle};
2323

2424
const BG_COLOR: Color = Color::rgb8(0x27, 0x28, 0x22);
2525
const FG_COLOR: Color = Color::rgb8(0xf0, 0xf0, 0xea);
@@ -116,8 +116,9 @@ impl WinHandler for PerfTest {
116116
}
117117

118118
fn main() {
119-
let mut app = Application::new(None);
120-
let mut builder = WindowBuilder::new();
119+
let state = AppState::new();
120+
let mut app = Application::new(state.clone(), None);
121+
let mut builder = WindowBuilder::new(state);
121122
let perf_test = PerfTest {
122123
size: Default::default(),
123124
handle: Default::default(),

druid-shell/examples/shello.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ use druid_shell::kurbo::{Line, Rect, Vec2};
1818
use druid_shell::piet::{Color, RenderContext};
1919

2020
use druid_shell::{
21-
Application, Cursor, FileDialogOptions, FileSpec, HotKey, KeyEvent, KeyModifiers, Menu,
22-
MouseEvent, SysMods, TimerToken, WinHandler, WindowBuilder, WindowHandle,
21+
AppState, Application, Cursor, FileDialogOptions, FileSpec, HotKey, KeyEvent, KeyModifiers,
22+
Menu, MouseEvent, SysMods, TimerToken, WinHandler, WindowBuilder, WindowHandle,
2323
};
2424

2525
const BG_COLOR: Color = Color::rgb8(0x27, 0x28, 0x22);
@@ -129,8 +129,9 @@ fn main() {
129129
menubar.add_dropdown(Menu::new(), "Application", true);
130130
menubar.add_dropdown(file_menu, "&File", true);
131131

132-
let mut app = Application::new(None);
133-
let mut builder = WindowBuilder::new();
132+
let state = AppState::new();
133+
let mut app = Application::new(state.clone(), None);
134+
let mut builder = WindowBuilder::new(state);
134135
builder.set_handler(Box::new(HelloState::default()));
135136
builder.set_title("Hello example");
136137
builder.set_menu(menubar);

druid-shell/src/application.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
//! The top-level application type.
1616
17+
use std::sync::atomic::{AtomicBool, Ordering};
18+
1719
use crate::clipboard::Clipboard;
1820
use crate::platform::application as platform;
1921

@@ -36,14 +38,43 @@ pub trait AppHandler {
3638
fn command(&mut self, id: u32) {}
3739
}
3840

41+
/// The top level application state.
42+
///
43+
/// This helps the application track all the state that it has created,
44+
/// which it later needs to clean up.
45+
#[derive(Clone)]
46+
pub struct AppState(pub(crate) platform::AppState);
47+
48+
impl AppState {
49+
/// Create a new `AppState` instance.
50+
pub fn new() -> AppState {
51+
AppState(platform::AppState::new())
52+
}
53+
}
54+
3955
//TODO: we may want to make the user create an instance of this (Application::global()?)
4056
//but for now I'd like to keep changes minimal.
4157
/// The top level application object.
4258
pub struct Application(platform::Application);
4359

60+
// Used to ensure only one Application instance is ever created.
61+
// This may change in the future.
62+
// For more information see https://github.com/xi-editor/druid/issues/771
63+
static APPLICATION_CREATED: AtomicBool = AtomicBool::new(false);
64+
4465
impl Application {
45-
pub fn new(handler: Option<Box<dyn AppHandler>>) -> Application {
46-
Application(platform::Application::new(handler))
66+
/// Create a new `Application`.
67+
///
68+
/// It takes the application `state` and a `handler` which will be used to inform of events.
69+
///
70+
/// Right now only one application can be created. See [druid#771] for discussion.
71+
///
72+
/// [druid#771]: https://github.com/xi-editor/druid/issues/771
73+
pub fn new(state: AppState, handler: Option<Box<dyn AppHandler>>) -> Application {
74+
if APPLICATION_CREATED.compare_and_swap(false, true, Ordering::AcqRel) {
75+
panic!("The Application instance has already been created.");
76+
}
77+
Application(platform::Application::new(state.0, handler))
4778
}
4879

4980
/// Start the runloop.

druid-shell/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ mod mouse;
3737
mod platform;
3838
mod window;
3939

40-
pub use application::{AppHandler, Application};
40+
pub use application::{AppHandler, AppState, Application};
4141
pub use clipboard::{Clipboard, ClipboardFormat, FormatId};
4242
pub use common_util::Counter;
4343
pub use dialog::{FileDialogOptions, FileInfo, FileSpec};

druid-shell/src/platform/gtk/application.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,19 @@ thread_local!(
3131
static GTK_APPLICATION: RefCell<Option<GtkApplication>> = RefCell::new(None);
3232
);
3333

34+
#[derive(Clone)]
35+
pub struct AppState;
36+
3437
pub struct Application;
3538

39+
impl AppState {
40+
pub(crate) fn new() -> AppState {
41+
AppState
42+
}
43+
}
44+
3645
impl Application {
37-
pub fn new(_handler: Option<Box<dyn AppHandler>>) -> Application {
46+
pub fn new(_state: AppState, _handler: Option<Box<dyn AppHandler>>) -> Application {
3847
// TODO: we should give control over the application ID to the user
3948
let application = GtkApplication::new(
4049
Some("com.github.xi-editor.druid"),

druid-shell/src/platform/gtk/window.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use gtk::{AccelGroup, ApplicationWindow};
3333
use crate::kurbo::{Point, Size, Vec2};
3434
use crate::piet::{Piet, RenderContext};
3535

36-
use super::application::with_application;
36+
use super::application::{with_application, AppState};
3737
use super::dialog;
3838
use super::menu::Menu;
3939
use super::util::assert_main_thread;
@@ -111,7 +111,7 @@ pub(crate) struct WindowState {
111111
}
112112

113113
impl WindowBuilder {
114-
pub fn new() -> WindowBuilder {
114+
pub fn new(_app_state: AppState) -> WindowBuilder {
115115
WindowBuilder {
116116
handler: None,
117117
title: String::new(),

druid-shell/src/platform/mac/application.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,21 @@ use crate::application::AppHandler;
3232

3333
static APP_HANDLER_IVAR: &str = "druidAppHandler";
3434

35+
#[derive(Clone)]
36+
pub struct AppState;
37+
3538
pub struct Application {
3639
ns_app: id,
3740
}
3841

42+
impl AppState {
43+
pub(crate) fn new() -> AppState {
44+
AppState
45+
}
46+
}
47+
3948
impl Application {
40-
pub fn new(handler: Option<Box<dyn AppHandler>>) -> Application {
49+
pub fn new(_state: AppState, handler: Option<Box<dyn AppHandler>>) -> Application {
4150
util::assert_main_thread();
4251
unsafe {
4352
let _pool = NSAutoreleasePool::new(nil);

druid-shell/src/platform/mac/window.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ use log::{error, info};
4141
use crate::kurbo::{Point, Size, Vec2};
4242
use crate::piet::{Piet, RenderContext};
4343

44+
use super::application::AppState;
4445
use super::dialog;
4546
use super::menu::Menu;
4647
use super::util::{assert_main_thread, make_nsstring};
@@ -104,7 +105,7 @@ struct ViewState {
104105
}
105106

106107
impl WindowBuilder {
107-
pub fn new() -> WindowBuilder {
108+
pub fn new(_app_state: AppState) -> WindowBuilder {
108109
WindowBuilder {
109110
handler: None,
110111
title: String::new(),

druid-shell/src/platform/web/application.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,19 @@
1717
use super::clipboard::Clipboard;
1818
use crate::application::AppHandler;
1919

20+
#[derive(Clone)]
21+
pub struct AppState;
22+
2023
pub struct Application;
2124

25+
impl AppState {
26+
pub(crate) fn new() -> AppState {
27+
AppState
28+
}
29+
}
30+
2231
impl Application {
23-
pub fn new(_handler: Option<Box<dyn AppHandler>>) -> Application {
32+
pub fn new(_state: AppState, _handler: Option<Box<dyn AppHandler>>) -> Application {
2433
Application
2534
}
2635

druid-shell/src/platform/web/window.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ use crate::kurbo::{Point, Size, Vec2};
2929

3030
use crate::piet::RenderContext;
3131

32+
use super::application::AppState;
3233
use super::error::Error;
3334
use super::keycodes::key_to_text;
3435
use super::menu::Menu;
@@ -296,7 +297,7 @@ fn setup_web_callbacks(window_state: &Rc<WindowState>) {
296297
}
297298

298299
impl WindowBuilder {
299-
pub fn new() -> WindowBuilder {
300+
pub fn new(_app_state: AppState) -> WindowBuilder {
300301
WindowBuilder {
301302
handler: None,
302303
title: String::new(),

0 commit comments

Comments
 (0)