Skip to content

Commit

Permalink
Rework blocking functions (#1336)
Browse files Browse the repository at this point in the history
Co-authored-by: Colin Rofls <colin@cmyr.net>
  • Loading branch information
JAicewizard and cmyr committed Oct 23, 2020
1 parent 62af6c4 commit 9d166f6
Showing 1 changed file with 46 additions and 38 deletions.
84 changes: 46 additions & 38 deletions druid/examples/blocking_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,45 +12,73 @@
// See the License for the specific language governing permissions and
// limitations under the License.

//! An example of a blocking function running in another thread.
//! An example of a blocking function running in another thread. We give
//! the other thread some data and then we also pass some data back
//! to the main thread using commands.

use std::{thread, time};

use druid::widget::prelude::*;
use druid::widget::{Button, Either, Flex, Label, Spinner};
use druid::{
AppDelegate, AppLauncher, Command, Data, DelegateCtx, Env, ExtEventSink, Lens, LocalizedString,
Selector, Widget, WidgetExt, WindowDesc,
};

use druid::{
widget::{Button, Either, Flex, Label, Spinner},
Handled, Target,
AppDelegate, AppLauncher, Command, Data, DelegateCtx, ExtEventSink, Handled, Lens,
LocalizedString, Selector, Target, WidgetExt, WindowDesc,
};

const FINISH_SLOW_FUNCTION: Selector<u32> = Selector::new("finish_slow_function");

struct Delegate {}

#[derive(Clone, Default, Data, Lens)]
struct AppState {
processing: bool,
value: u32,
}

// Pretend this is downloading a file, or doing heavy calculations...
fn slow_function(number: u32) -> u32 {
let a_while = time::Duration::from_millis(2000);
thread::sleep(a_while);
number + 1
fn ui_builder() -> impl Widget<AppState> {
let button = Button::new("Start slow increment")
.on_click(|ctx, data: &mut AppState, _env| {
data.processing = true;
// In order to make sure that the other thread can communicate with the main thread we
// have to pass an external handle to the second thread.
// Using this handle we can send commands back to the main thread.
wrapped_slow_function(ctx.get_external_handle(), data.value);
})
.padding(5.0);

let button_placeholder = Flex::column()
.with_child(Label::new(LocalizedString::new("Processing...")).padding(5.0))
.with_child(Spinner::new());

// Hello-counter is defined in the built-in localisation file. This maps to "Current value is {count}"
// localised in english, french, or german. Every time the value is updated it shows the new value.
let text = LocalizedString::new("hello-counter")
.with_arg("count", |data: &AppState, _env| (data.value).into());
let label = Label::new(text).padding(5.0).center();

let either = Either::new(|data, _env| data.processing, button_placeholder, button);

Flex::column().with_child(label).with_child(either)
}

fn wrapped_slow_function(sink: ExtEventSink, number: u32) {
thread::spawn(move || {
let number = slow_function(number);
// Once the slow function is done we can use the event sink (the external handle).
// This sends the `FINISH_SLOW_FUNCTION` command to the main thread and attach
// the number as payload.
sink.submit_command(FINISH_SLOW_FUNCTION, number, Target::Auto)
.expect("command failed to submit");
});
}

// Pretend this is downloading a file, or doing heavy calculations...
fn slow_function(number: u32) -> u32 {
let a_while = time::Duration::from_millis(2000);
thread::sleep(a_while);
number + 1
}

struct Delegate;

impl AppDelegate<AppState> for Delegate {
fn command(
&mut self,
Expand All @@ -61,6 +89,7 @@ impl AppDelegate<AppState> for Delegate {
_env: &Env,
) -> Handled {
if let Some(number) = cmd.get(FINISH_SLOW_FUNCTION) {
// If the command we received is `FINISH_SLOW_FUNCTION` handle the payload.
data.processing = false;
data.value = *number;
Handled::Yes
Expand All @@ -70,31 +99,10 @@ impl AppDelegate<AppState> for Delegate {
}
}

fn ui_builder() -> impl Widget<AppState> {
let button = Button::new("Start slow increment")
.on_click(|ctx, data: &mut AppState, _env| {
data.processing = true;
wrapped_slow_function(ctx.get_external_handle(), data.value);
})
.padding(5.0);
let button_placeholder = Flex::column()
.with_child(Label::new(LocalizedString::new("Processing...")).padding(5.0))
.with_child(Spinner::new());

let text = LocalizedString::new("hello-counter")
.with_arg("count", |data: &AppState, _env| (data.value).into());
let label = Label::new(text).padding(5.0).center();

let either = Either::new(|data, _env| data.processing, button_placeholder, button);

Flex::column().with_child(label).with_child(either)
}
fn main() {
let main_window = WindowDesc::new(ui_builder).title(LocalizedString::new("Blocking functions"));
let app = AppLauncher::with_window(main_window);
let delegate = Delegate {};
app.delegate(delegate)
.use_simple_logger()
AppLauncher::with_window(main_window)
.delegate(Delegate {})
.launch(AppState::default())
.expect("launch failed");
}

0 comments on commit 9d166f6

Please sign in to comment.