Skip to content

Commit

Permalink
Move app quit code to the application module.
Browse files Browse the repository at this point in the history
  • Loading branch information
xStrom committed Apr 27, 2020
1 parent 0039e3c commit c566164
Show file tree
Hide file tree
Showing 24 changed files with 392 additions and 459 deletions.
6 changes: 3 additions & 3 deletions druid-shell/examples/invalidate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ impl WinHandler for InvalidateTest {
}

fn main() {
let mut app = Application::new(None);
let mut builder = WindowBuilder::new();
let app = Application::new();
let mut builder = WindowBuilder::new(app.clone());
let inv_test = InvalidateTest {
size: Default::default(),
handle: Default::default(),
Expand All @@ -98,5 +98,5 @@ fn main() {

let window = builder.build().unwrap();
window.show();
app.run();
app.run(None);
}
11 changes: 5 additions & 6 deletions druid-shell/examples/perftest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use time::Instant;
use piet_common::kurbo::{Line, Rect};
use piet_common::{Color, FontBuilder, Piet, RenderContext, Text, TextLayoutBuilder};

use druid_shell::{AppState, Application, KeyEvent, WinHandler, WindowBuilder, WindowHandle};
use druid_shell::{Application, KeyEvent, WinHandler, WindowBuilder, WindowHandle};

const BG_COLOR: Color = Color::rgb8(0x27, 0x28, 0x22);
const FG_COLOR: Color = Color::rgb8(0xf0, 0xf0, 0xea);
Expand Down Expand Up @@ -107,7 +107,7 @@ impl WinHandler for PerfTest {
}

fn destroy(&mut self) {
Application::quit()
Application::global().quit()
}

fn as_any(&mut self) -> &mut dyn Any {
Expand All @@ -116,9 +116,8 @@ impl WinHandler for PerfTest {
}

fn main() {
let state = AppState::new();
let mut app = Application::new(state.clone(), None);
let mut builder = WindowBuilder::new(state);
let app = Application::new();
let mut builder = WindowBuilder::new(app.clone());
let perf_test = PerfTest {
size: Default::default(),
handle: Default::default(),
Expand All @@ -131,5 +130,5 @@ fn main() {
let window = builder.build().unwrap();
window.show();

app.run();
app.run(None);
}
15 changes: 7 additions & 8 deletions druid-shell/examples/shello.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ use druid_shell::kurbo::{Line, Rect, Vec2};
use druid_shell::piet::{Color, RenderContext};

use druid_shell::{
AppState, Application, Cursor, FileDialogOptions, FileSpec, HotKey, KeyEvent, KeyModifiers,
Menu, MouseEvent, SysMods, TimerToken, WinHandler, WindowBuilder, WindowHandle,
Application, Cursor, FileDialogOptions, FileSpec, HotKey, KeyEvent, KeyModifiers, Menu,
MouseEvent, SysMods, TimerToken, WinHandler, WindowBuilder, WindowHandle,
};

const BG_COLOR: Color = Color::rgb8(0x27, 0x28, 0x22);
Expand Down Expand Up @@ -48,7 +48,7 @@ impl WinHandler for HelloState {
match id {
0x100 => {
self.handle.close();
Application::quit();
Application::global().quit()
}
0x101 => {
let options = FileDialogOptions::new().show_hidden().allowed_types(vec![
Expand Down Expand Up @@ -101,7 +101,7 @@ impl WinHandler for HelloState {
}

fn destroy(&mut self) {
Application::quit()
Application::global().quit()
}

fn as_any(&mut self) -> &mut dyn Any {
Expand Down Expand Up @@ -129,15 +129,14 @@ fn main() {
menubar.add_dropdown(Menu::new(), "Application", true);
menubar.add_dropdown(file_menu, "&File", true);

let state = AppState::new();
let mut app = Application::new(state.clone(), None);
let mut builder = WindowBuilder::new(state);
let app = Application::new();
let mut builder = WindowBuilder::new(app.clone());
builder.set_handler(Box::new(HelloState::default()));
builder.set_title("Hello example");
builder.set_menu(menubar);

let window = builder.build().unwrap();
window.show();

app.run();
app.run(None);
}
146 changes: 112 additions & 34 deletions druid-shell/src/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@

//! The top-level application type.
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::atomic::{AtomicBool, Ordering};

use crate::clipboard::Clipboard;
use crate::platform::application as platform;
use crate::util;

/// A top-level handler that is not associated with any window.
///
Expand All @@ -38,73 +41,148 @@ pub trait AppHandler {
fn command(&mut self, id: u32) {}
}

/// The top level application state.
/// The top level application object.
///
/// This helps the application track all the state that it has created,
/// which it later needs to clean up.
/// This can be thought of as a reference and it can be safely cloned.
#[derive(Clone)]
pub struct AppState(pub(crate) platform::AppState);

impl AppState {
/// Create a new `AppState` instance.
pub fn new() -> AppState {
AppState(platform::AppState::new())
}
pub struct Application {
state: Rc<RefCell<State>>,
pub(crate) platform_app: platform::Application,
}

//TODO: we may want to make the user create an instance of this (Application::global()?)
//but for now I'd like to keep changes minimal.
/// The top level application object.
pub struct Application(platform::Application);
/// Platform-independent `Application` state.
struct State {
running: bool,
}

// Used to ensure only one Application instance is ever created.
// This may change in the future.
// For more information see https://github.com/xi-editor/druid/issues/771
/// Used to ensure only one Application instance is ever created.
static APPLICATION_CREATED: AtomicBool = AtomicBool::new(false);

thread_local! {
/// A reference object to the current `Application`, if any.
static GLOBAL_APP: RefCell<Option<Application>> = RefCell::new(None);
}

impl Application {
/// Create a new `Application`.
///
/// It takes the application `state` and a `handler` which will be used to inform of events.
/// # Panics
///
/// Panics if an `Application` has already been created.
///
/// Right now only one application can be created. See [druid#771] for discussion.
/// This may change in the future. See [druid#771] for discussion.
///
/// [druid#771]: https://github.com/xi-editor/druid/issues/771
pub fn new(state: AppState, handler: Option<Box<dyn AppHandler>>) -> Application {
pub fn new() -> Application {
if APPLICATION_CREATED.compare_and_swap(false, true, Ordering::AcqRel) {
panic!("The Application instance has already been created.");
}
Application(platform::Application::new(state.0, handler))
util::claim_main_thread();
let state = Rc::new(RefCell::new(State { running: false }));
let app = Application {
state,
platform_app: platform::Application::new(),
};
GLOBAL_APP.with(|global_app| {
*global_app.borrow_mut() = Some(app.clone());
});
app
}

/// Start the runloop.
/// Get the current globally active `Application`.
///
/// This will block the current thread until the program has finished executing.
pub fn run(&mut self) {
self.0.run()
/// A globally active `Application` exists
/// after [`new`] is called and until [`run`] returns.
///
/// # Panics
///
/// Panics if there is no globally active `Application`.
/// For a non-panicking function use [`try_global`].
///
/// This function will also panic if called from a non-main thread.
///
/// [`new`]: #method.new
/// [`run`]: #method.run
/// [`try_global`]: #method.try_global
#[inline]
pub fn global() -> Application {
// Main thread assertion takes place in try_global()
Application::try_global().expect("There is no globally active Application")
}

/// Terminate the application.
pub fn quit() {
platform::Application::quit()
/// Get the current globally active `Application`.
///
/// A globally active `Application` exists
/// after [`new`] is called and until [`run`] returns.
///
/// # Panics
///
/// Panics if called from a non-main thread.
///
/// [`new`]: #method.new
/// [`run`]: #method.run
pub fn try_global() -> Option<Application> {
util::assert_main_thread();
GLOBAL_APP.with(|global_app| global_app.borrow().clone())
}

/// Start the `Application` runloop.
///
/// The provided `handler` will be used to inform of events.
///
/// This will consume the `Application` and block the current thread
/// until the `Application` has finished executing.
///
/// # Panics
///
/// Panics if the `Application` is already running.
pub fn run(self, handler: Option<Box<dyn AppHandler>>) {
// Make sure this application hasn't run() yet.
if let Ok(mut state) = self.state.try_borrow_mut() {
if state.running {
panic!("Application is already running");
}
state.running = true;
} else {
panic!("Application state already borrowed");
}

// Run the platform application
self.platform_app.run(handler);

// This application is no longer active, so clear the global reference
GLOBAL_APP.with(|global_app| {
*global_app.borrow_mut() = None;
});
// .. and release the main thread
util::release_main_thread();
}

/// Quit the `Application`.
///
/// This will cause [`run`] to return control back to the calling function.
///
/// [`run`]: #method.run
pub fn quit(&self) {
self.platform_app.quit()
}

// TODO: do these two go in some kind of PlatformExt trait?
/// Hide the application this window belongs to. (cmd+H)
pub fn hide() {
pub fn hide(&self) {
#[cfg(target_os = "macos")]
platform::Application::hide()
self.platform_app.hide()
}

/// Hide all other applications. (cmd+opt+H)
pub fn hide_others() {
pub fn hide_others(&self) {
#[cfg(target_os = "macos")]
platform::Application::hide_others()
self.platform_app.hide_others()
}

/// Returns a handle to the system clipboard.
pub fn clipboard() -> Clipboard {
platform::Application::clipboard().into()
pub fn clipboard(&self) -> Clipboard {
self.platform_app.clipboard().into()
}

/// Returns the current locale string.
Expand Down
6 changes: 3 additions & 3 deletions druid-shell/src/clipboard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ pub use crate::platform::clipboard as platform;
/// ```no_run
/// use druid_shell::{Application, Clipboard};
///
/// let mut clipboard = Application::clipboard();
/// let mut clipboard = Application::global().clipboard();
/// clipboard.put_string("watch it there pal");
/// if let Some(contents) = clipboard.get_string() {
/// assert_eq!("what it there pal", contents.as_str());
Expand All @@ -83,7 +83,7 @@ pub use crate::platform::clipboard as platform;
/// ```no_run
/// use druid_shell::{Application, Clipboard, ClipboardFormat};
///
/// let mut clipboard = Application::clipboard();
/// let mut clipboard = Application::global().clipboard();
///
/// let custom_type_id = "io.xieditor.path-clipboard-type";
///
Expand All @@ -104,7 +104,7 @@ pub use crate::platform::clipboard as platform;
/// ```no_run
/// use druid_shell::{Application, Clipboard, ClipboardFormat};
///
/// let clipboard = Application::clipboard();
/// let clipboard = Application::global().clipboard();
///
/// let custom_type_id = "io.xieditor.path-clipboard-type";
/// let supported_types = &[custom_type_id, ClipboardFormat::SVG, ClipboardFormat::PDF];
Expand Down
3 changes: 2 additions & 1 deletion druid-shell/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ mod keycodes;
mod menu;
mod mouse;
mod platform;
mod util;
mod window;

pub use application::{AppHandler, AppState, Application};
pub use application::{AppHandler, Application};
pub use clipboard::{Clipboard, ClipboardFormat, FormatId};
pub use common_util::Counter;
pub use dialog::{FileDialogOptions, FileInfo, FileSpec};
Expand Down
18 changes: 5 additions & 13 deletions druid-shell/src/platform/gtk/application.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,10 @@ thread_local!(
);

#[derive(Clone)]
pub struct AppState;

pub struct Application;

impl AppState {
pub(crate) fn new() -> AppState {
AppState
}
}
pub(crate) struct Application;

impl Application {
pub fn new(_state: AppState, _handler: Option<Box<dyn AppHandler>>) -> Application {
pub fn new() -> Application {
// TODO: we should give control over the application ID to the user
let application = GtkApplication::new(
Some("com.github.xi-editor.druid"),
Expand All @@ -68,7 +60,7 @@ impl Application {
Application
}

pub fn run(&mut self) {
pub fn run(self, _handler: Option<Box<dyn AppHandler>>) {
util::assert_main_thread();

// TODO: should we pass the command line arguments?
Expand All @@ -80,7 +72,7 @@ impl Application {
});
}

pub fn quit() {
pub fn quit(&self) {
util::assert_main_thread();
with_application(|app| {
match app.get_active_window() {
Expand All @@ -95,7 +87,7 @@ impl Application {
});
}

pub fn clipboard() -> Clipboard {
pub fn clipboard(&self) -> Clipboard {
Clipboard
}

Expand Down
Loading

0 comments on commit c566164

Please sign in to comment.