implement background message handler (worker)

This commit is contained in:
2022-05-22 13:27:53 +02:00
parent 6366932aca
commit 1487ad5bd3
11 changed files with 323 additions and 34 deletions

View File

@@ -1,9 +1,6 @@
use std::collections::VecDeque;
use relm4::{
factory::{FactoryVecDeque},
RelmApp,
};
use relm4::{factory::FactoryVecDeque, RelmApp};
use toolbx::ToolbxContainer;
use ui::app::model::AppModel;
@@ -13,7 +10,7 @@ mod ui;
fn main() {
let toolbx_list = VecDeque::from(ToolbxContainer::get_toolboxes());
let factory_vec = FactoryVecDeque::new();
let mut model = AppModel {
toolboxes: factory_vec,
};

View File

@@ -1,7 +1,10 @@
use relm4::factory::DynamicIndex;
use super::model::ToolbxEntry;
pub enum AppMsg {
ShowToolboxSettingsRequest,
ShowToolboxAppsRequest,
ToolbxContainerToggleStartStop(DynamicIndex),
ToolbxContainerChanged(DynamicIndex, ToolbxEntry),
}

View File

@@ -3,3 +3,4 @@ pub mod model;
pub mod toolbox_list;
pub mod update;
pub mod widgets;
pub mod workers;

View File

@@ -1,12 +1,10 @@
use relm4::{
factory::{FactoryVecDeque},
Model,
};
use relm4::{factory::FactoryVecDeque, Model};
use crate::{toolbx::ToolbxContainer, ui::components::AppComponents};
use super::{messages::AppMsg, widgets::AppWidgets};
#[derive(Debug, Clone)]
pub struct ToolbxEntry {
pub toolbx_container: ToolbxContainer,
pub changing_status: bool,
@@ -14,16 +12,23 @@ pub struct ToolbxEntry {
}
impl ToolbxEntry {
pub fn update_container(&mut self, container : ToolbxContainer) {
pub fn update_container(&mut self, container: ToolbxContainer) {
std::mem::replace::<ToolbxContainer>(&mut self.toolbx_container, container);
}
pub fn update_entry(&mut self, container: ToolbxEntry) {
std::mem::replace::<ToolbxContainer>(
&mut self.toolbx_container,
container.toolbx_container,
);
self.changing_status = container.changing_status;
}
}
pub struct AppModel {
pub toolboxes: FactoryVecDeque<ToolbxEntry>,
}
impl Model for AppModel {
impl Model for AppModel {
type Msg = AppMsg;
type Widgets = AppWidgets;
type Components = AppComponents;
@@ -32,7 +37,8 @@ impl Model for AppModel {
impl AppModel {
pub fn update_toolbxes<I>(&mut self, toolbox_iter: I)
where
I: Iterator<Item = ToolbxContainer>, {
I: Iterator<Item = ToolbxContainer>,
{
// Update each toolbx entry if there were changes to it
// TODO: deal with the removal of toolboxes
for tbx_update in toolbox_iter {
@@ -40,15 +46,20 @@ impl AppModel {
let mut exists = false;
for (index, tbx_entry) in self.toolboxes.iter().enumerate() {
if tbx_update.name == tbx_entry.toolbx_container.name {
self.toolboxes.get_mut(index).map(|x| x.update_container(tbx_update.clone()));
self.toolboxes
.get_mut(index)
.map(|x| x.update_container(tbx_update.clone()));
exists = true;
break;
}
}
if !exists {
println!("{}", tbx_update.name);
self.toolboxes.push_back(ToolbxEntry { toolbx_container: tbx_update, changing_status: false})
self.toolboxes.push_back(ToolbxEntry {
toolbx_container: tbx_update,
changing_status: false,
})
}
}
}
}
}

View File

@@ -9,7 +9,7 @@ use relm4::{
};
use crate::{
toolbx::{ToolbxContainer, ToolbxStatus},
toolbx::ToolbxStatus,
ui::ui_strings::{
APP_ICON, APP_TOOLTIP, SETTINGS_ICON, SETTINGS_TOOLTIP, SHUTDOWN_ICON, SHUTDOWN_TOOLTIP,
START_ICON, START_TOOLTIP, TERMINAL_ICON, TERMINAL_TOOLTIP, UPDATE_ICON, UPDATE_TOOLTIP,
@@ -33,7 +33,6 @@ impl FactoryPrototype for ToolbxEntry {
type Msg = AppMsg;
fn init_view(&self, key: &DynamicIndex, sender: Sender<Self::Msg>) -> Self::Widgets {
view! {
suffix_box = &gtk::Box{
append = &gtk::AspectFrame{
@@ -107,7 +106,10 @@ impl FactoryPrototype for ToolbxEntry {
}
}
let subtitle = format!("created {}\n{}", self.toolbx_container.created, self.toolbx_container.image);
let subtitle = format!(
"created {}\n{}",
self.toolbx_container.created, self.toolbx_container.image
);
let index = key.clone();
@@ -117,7 +119,7 @@ impl FactoryPrototype for ToolbxEntry {
set_margin_bottom: 10,
set_tooltip_text: Some(status_button_tooltip),
set_css_classes: &["circular"],
}
};
//status_spinner.start();
@@ -150,7 +152,11 @@ impl FactoryPrototype for ToolbxEntry {
}
};
FactoryWidgets { action_row, status_button, status_spinner}
FactoryWidgets {
action_row,
status_button,
status_spinner,
}
}
fn view(
@@ -160,19 +166,21 @@ impl FactoryPrototype for ToolbxEntry {
) {
println!("updated {}", key.current_index());
// fixme: IDEALY this is would be done with message handling and only if the request actually is done
if self.changing_status {
widgets.status_button.set_sensitive(false);
widgets.status_button.set_child(Some(&widgets.status_spinner));
widgets
.status_button
.set_child(Some(&widgets.status_spinner));
widgets.status_spinner.start();
} else {
match self.toolbx_container.status {
ToolbxStatus::Running => {
widgets.status_button.set_icon_name(SHUTDOWN_ICON);
widgets.status_button.set_tooltip_text(Some(SHUTDOWN_TOOLTIP));
widgets
.status_button
.set_tooltip_text(Some(SHUTDOWN_TOOLTIP));
}
_ => {
widgets.status_button.set_icon_name(START_ICON);
@@ -182,8 +190,6 @@ impl FactoryPrototype for ToolbxEntry {
widgets.status_button.set_sensitive(true);
widgets.status_spinner.stop();
}
}
fn root_widget(widgets: &Self::Widgets) -> &Self::Root {

View File

@@ -8,7 +8,7 @@ use crate::{
},
};
use super::{messages::AppMsg, model::AppModel};
use super::{messages::AppMsg, model::AppModel, workers::AsyncHandlerMsg};
impl AppUpdate for AppModel {
fn update(&mut self, msg: AppMsg, components: &AppComponents, _sender: Sender<AppMsg>) -> bool {
@@ -27,23 +27,38 @@ impl AppUpdate for AppModel {
}
AppMsg::ToolbxContainerToggleStartStop(index) => {
if let Some(toolbx_container) = self.toolboxes.get_mut(index.current_index()) {
match toolbx_container.toolbx_container.status {
ToolbxStatus::Exited | ToolbxStatus::Configured => {
toolbx_container.changing_status = true;
// Send message to background worker to start
//toolbx_container.toolbx_container.start();
components
.async_handler
.sender()
.blocking_send(AsyncHandlerMsg::StartToolbx(
index,
toolbx_container.clone(),
))
.expect("Receiver dropped");
}
ToolbxStatus::Running => {
toolbx_container.changing_status = true;
// send message to beackground worker to stop
//toolbx_container.toolbx_container.stop();
components
.async_handler
.sender()
.blocking_send(AsyncHandlerMsg::StopToolbx(
index,
toolbx_container.clone(),
))
.expect("Receiver dropped");
}
}
// TODO: tell button to reactivate somehow
}
}
AppMsg::ToolbxContainerChanged(index, container) => {
if let Some(toolbx_container) = self.toolboxes.get_mut(index.current_index()) {
toolbx_container.update_entry(container);
}
}
}
true
}

View File

@@ -4,7 +4,7 @@ use relm4::{
prelude::{BoxExt, GtkWindowExt, OrientableExt, WidgetExt},
traits::AdwApplicationWindowExt,
},
gtk::{self, SelectionMode, Align, PolicyType},
gtk::{self, Align, PolicyType, SelectionMode},
WidgetPlus, Widgets,
};

67
src/ui/app/workers.rs Normal file
View File

@@ -0,0 +1,67 @@
use relm4::factory::DynamicIndex;
use relm4::{send, MessageHandler, Sender};
use tokio::runtime::{Builder, Runtime};
use tokio::sync::mpsc::{channel, Sender as TokioSender};
use super::{
messages::AppMsg,
model::{AppModel, ToolbxEntry},
};
// Code adapted from https://relm4.org/book/stable/message_handler.html
pub struct AsyncHandler {
_rt: Runtime,
sender: TokioSender<AsyncHandlerMsg>,
}
#[derive(Debug)]
pub enum AsyncHandlerMsg {
StopToolbx(DynamicIndex, ToolbxEntry),
StartToolbx(DynamicIndex, ToolbxEntry),
}
impl MessageHandler<AppModel> for AsyncHandler {
type Msg = AsyncHandlerMsg;
type Sender = TokioSender<AsyncHandlerMsg>;
fn init(_parent_model: &AppModel, parent_sender: Sender<AppMsg>) -> Self {
let (sender, mut rx) = channel::<AsyncHandlerMsg>(10);
let rt = Builder::new_multi_thread()
.worker_threads(8)
.enable_time()
.build()
.unwrap();
rt.spawn(async move {
while let Some(msg) = rx.recv().await {
let parent_sender = parent_sender.clone();
tokio::spawn(async move {
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
match msg {
AsyncHandlerMsg::StopToolbx(index, mut tbx) => {
tbx.toolbx_container.stop();
tbx.changing_status = false;
send! {parent_sender, AppMsg::ToolbxContainerChanged(index, tbx)};
}
AsyncHandlerMsg::StartToolbx(index, mut tbx) => {
tbx.toolbx_container.start();
tbx.changing_status = false;
send! {parent_sender, AppMsg::ToolbxContainerChanged(index, tbx)};
}
}
});
}
});
AsyncHandler { _rt: rt, sender }
}
fn send(&self, msg: Self::Msg) {
self.sender.blocking_send(msg).unwrap();
}
fn sender(&self) -> Self::Sender {
self.sender.clone()
}
}

View File

@@ -1,4 +1,5 @@
use relm4::RelmComponent;
use relm4::RelmMsgHandler;
use relm4::Sender;
use self::{
@@ -6,6 +7,7 @@ use self::{
};
use super::app::model::AppModel;
use super::app::workers::AsyncHandler;
pub mod toolbox_apps;
pub mod toolbox_settings;
@@ -14,4 +16,5 @@ pub mod toolbox_settings;
pub struct AppComponents {
pub toolbox_settings_dialog: RelmComponent<ToolboxSettingsDialogModel, AppModel>,
pub toolbox_apps_dialog: RelmComponent<ToolboxAppDialogModel, AppModel>,
pub async_handler: RelmMsgHandler<AsyncHandler, AppModel>,
}