mirror of
https://github.com/13hannes11/toolbx-tuner.git
synced 2024-09-03 23:21:00 +02:00
implement background message handler (worker)
This commit is contained in:
@@ -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,
|
||||
};
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
use relm4::factory::DynamicIndex;
|
||||
|
||||
use super::model::ToolbxEntry;
|
||||
|
||||
pub enum AppMsg {
|
||||
ShowToolboxSettingsRequest,
|
||||
ShowToolboxAppsRequest,
|
||||
ToolbxContainerToggleStartStop(DynamicIndex),
|
||||
ToolbxContainerChanged(DynamicIndex, ToolbxEntry),
|
||||
}
|
||||
|
||||
@@ -3,3 +3,4 @@ pub mod model;
|
||||
pub mod toolbox_list;
|
||||
pub mod update;
|
||||
pub mod widgets;
|
||||
pub mod workers;
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 = >k::Box{
|
||||
append = >k::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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
67
src/ui/app/workers.rs
Normal 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()
|
||||
}
|
||||
}
|
||||
@@ -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>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user