Skip to content

Commit a43d1f1

Browse files
committed
Refactor GTK Application to make use of the new structure.
1 parent 40cbcee commit a43d1f1

File tree

3 files changed

+58
-73
lines changed

3 files changed

+58
-73
lines changed

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

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

1515
//! GTK implementation of features at the application scope.
1616
17-
use std::cell::RefCell;
18-
1917
use gio::prelude::ApplicationExtManual;
2018
use gio::{ApplicationExt, ApplicationFlags, Cancellable};
2119
use gtk::{Application as GtkApplication, GtkApplicationExt};
@@ -24,69 +22,59 @@ use crate::application::AppHandler;
2422

2523
use super::clipboard::Clipboard;
2624
use super::error::Error;
27-
use super::util;
28-
29-
// XXX: The application needs to be global because WindowBuilder::build wants
30-
// to construct an ApplicationWindow, which needs the application, but
31-
// WindowBuilder::build does not get the RunLoop
32-
thread_local!(
33-
static GTK_APPLICATION: RefCell<Option<GtkApplication>> = RefCell::new(None);
34-
);
3525

3626
#[derive(Clone)]
37-
pub(crate) struct Application;
27+
pub(crate) struct Application {
28+
gtk_app: GtkApplication,
29+
}
3830

3931
impl Application {
4032
pub fn new() -> Result<Application, Error> {
4133
// TODO: we should give control over the application ID to the user
42-
let application = GtkApplication::new(
34+
let gtk_app = match GtkApplication::new(
4335
Some("com.github.xi-editor.druid"),
4436
// TODO we set this to avoid connecting to an existing running instance
4537
// of "com.github.xi-editor.druid" after which we would never receive
4638
// the "Activate application" below. See pull request druid#384
4739
// Which shows another way once we have in place a mechanism for
4840
// communication with remote instances.
4941
ApplicationFlags::NON_UNIQUE,
50-
)
51-
.expect("Unable to create GTK application");
42+
) {
43+
Ok(app) => app,
44+
Err(err) => return Err(Error::BoolError(err)),
45+
};
5246

53-
application.connect_activate(|_app| {
47+
gtk_app.connect_activate(|_app| {
5448
log::info!("gtk: Activated application");
5549
});
5650

57-
application
58-
.register(None as Option<&Cancellable>)
59-
.expect("Could not register GTK application");
51+
if let Err(err) = gtk_app.register(None as Option<&Cancellable>) {
52+
return Err(Error::Error(err));
53+
}
6054

61-
GTK_APPLICATION.with(move |x| *x.borrow_mut() = Some(application));
62-
Ok(Application)
55+
Ok(Application { gtk_app })
6356
}
6457

65-
pub fn run(self, _handler: Option<Box<dyn AppHandler>>) {
66-
util::assert_main_thread();
58+
#[inline]
59+
pub fn gtk_app(&self) -> &GtkApplication {
60+
&self.gtk_app
61+
}
6762

63+
pub fn run(self, _handler: Option<Box<dyn AppHandler>>) {
6864
// TODO: should we pass the command line arguments?
69-
GTK_APPLICATION.with(|x| {
70-
x.borrow()
71-
.as_ref()
72-
.unwrap() // Safe because we initialized this in RunLoop::new
73-
.run(&[])
74-
});
65+
self.gtk_app.run(&[]);
7566
}
7667

7768
pub fn quit(&self) {
78-
util::assert_main_thread();
79-
with_application(|app| {
80-
match app.get_active_window() {
81-
None => {
82-
// no application is running, main is not running
83-
}
84-
Some(_) => {
85-
// we still have an active window, close the run loop
86-
app.quit();
87-
}
69+
match self.gtk_app.get_active_window() {
70+
None => {
71+
// no application is running, main is not running
8872
}
89-
});
73+
Some(_) => {
74+
// we still have an active window, close the run loop
75+
self.gtk_app.quit();
76+
}
77+
}
9078
}
9179

9280
pub fn clipboard(&self) -> Clipboard {
@@ -97,18 +85,3 @@ impl Application {
9785
glib::get_language_names()[0].as_str().into()
9886
}
9987
}
100-
101-
#[inline]
102-
pub(crate) fn with_application<F, R>(f: F) -> R
103-
where
104-
F: std::ops::FnOnce(GtkApplication) -> R,
105-
{
106-
util::assert_main_thread();
107-
GTK_APPLICATION.with(move |app| {
108-
let app = app
109-
.borrow()
110-
.clone()
111-
.expect("Tried to manipulate the application before RunLoop::new was called");
112-
f(app)
113-
})
114-
}

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

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,26 @@
1414

1515
//! GTK platform errors.
1616
17-
//TODO: add a platform error for GTK
17+
use std::fmt;
1818

19+
use glib::{BoolError, Error as GLibError};
20+
21+
/// GTK platform errors.
1922
#[derive(Debug, Clone)]
20-
pub struct Error;
23+
pub enum Error {
24+
/// Generic GTK error.
25+
Error(GLibError),
26+
/// GTK error that has no information provided by GTK,
27+
/// but may have extra information provided by gtk-rs.
28+
BoolError(BoolError),
29+
}
2130

22-
impl std::fmt::Display for Error {
23-
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
24-
write!(f, "GTK Error")
31+
impl fmt::Display for Error {
32+
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
33+
match self {
34+
Error::Error(err) => write!(f, "GTK Error: {}", err),
35+
Error::BoolError(err) => write!(f, "GTK BoolError: {}", err),
36+
}
2537
}
2638
}
2739

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

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

36-
use super::application::{with_application, Application};
37-
use super::dialog;
38-
use super::menu::Menu;
39-
use super::util::assert_main_thread;
40-
4136
use crate::common_util::IdleCallback;
4237
use crate::dialog::{FileDialogOptions, FileDialogType, FileInfo};
4338
use crate::keyboard;
4439
use crate::mouse::{Cursor, MouseButton, MouseEvent};
4540
use crate::window::{IdleToken, Text, TimerToken, WinHandler};
4641
use crate::Error;
4742

43+
use super::application::Application;
44+
use super::dialog;
45+
use super::menu::Menu;
46+
use super::util;
47+
4848
/// Taken from https://gtk-rs.org/docs-src/tutorial/closures
4949
/// It is used to reduce the boilerplate of setting up gtk callbacks
5050
/// Example:
@@ -82,6 +82,7 @@ pub struct WindowHandle {
8282

8383
/// Builder abstraction for creating new windows
8484
pub(crate) struct WindowBuilder {
85+
app: Application,
8586
handler: Option<Box<dyn WinHandler>>,
8687
title: String,
8788
menu: Option<Menu>,
@@ -112,8 +113,9 @@ pub(crate) struct WindowState {
112113
}
113114

114115
impl WindowBuilder {
115-
pub fn new(_app: Application) -> WindowBuilder {
116+
pub fn new(app: Application) -> WindowBuilder {
116117
WindowBuilder {
118+
app,
117119
handler: None,
118120
title: String::new(),
119121
menu: None,
@@ -153,13 +155,11 @@ impl WindowBuilder {
153155
}
154156

155157
pub fn build(self) -> Result<WindowHandle, Error> {
156-
assert_main_thread();
157-
158158
let handler = self
159159
.handler
160160
.expect("Tried to build a window without setting the handler");
161161

162-
let window = with_application(|app| ApplicationWindow::new(&app));
162+
let window = ApplicationWindow::new(self.app.gtk_app());
163163

164164
window.set_title(&self.title);
165165
window.set_resizable(self.resizable);
@@ -191,14 +191,14 @@ impl WindowBuilder {
191191
current_keyval: RefCell::new(None),
192192
});
193193

194-
with_application(|app| {
195-
app.connect_shutdown(clone!(win_state => move |_| {
194+
self.app
195+
.gtk_app()
196+
.connect_shutdown(clone!(win_state => move |_| {
196197
// this ties a clone of Arc<WindowState> to the ApplicationWindow to keep it alive
197198
// when the ApplicationWindow is destroyed, the last Arc is dropped
198199
// and any Weak<WindowState> will be None on upgrade()
199200
let _ = &win_state;
200-
}))
201-
});
201+
}));
202202

203203
let handle = WindowHandle {
204204
state: Arc::downgrade(&win_state),
@@ -734,7 +734,7 @@ impl IdleHandle {
734734
}
735735

736736
fn run_idle(state: &Arc<WindowState>) -> glib::source::Continue {
737-
assert_main_thread();
737+
util::assert_main_thread();
738738
let mut handler = state.handler.borrow_mut();
739739

740740
let queue: Vec<_> = std::mem::replace(&mut state.idle_queue.lock().unwrap(), Vec::new());

0 commit comments

Comments
 (0)