From c02ff988c1734b7d80cc826b3cc88cab52752bc8 Mon Sep 17 00:00:00 2001 From: LordMZTE Date: Mon, 8 Nov 2021 21:50:29 +0100 Subject: [PATCH] prevent all lights having their state set on startup + some cleanup --- src/gui/headerbar.rs | 5 ++-- src/gui/light_entry.rs | 54 ++++++++++++++++++++++++++++++++--------- src/gui/mod.rs | 2 +- src/gui/settings.rs | 55 ++++++++++++++++++++++++++---------------- src/runtime.rs | 41 +++++++++++++++++++++++++------ 5 files changed, 114 insertions(+), 43 deletions(-) diff --git a/src/gui/headerbar.rs b/src/gui/headerbar.rs index 34bd788..b636c85 100644 --- a/src/gui/headerbar.rs +++ b/src/gui/headerbar.rs @@ -23,13 +23,14 @@ pub enum HeaderBarMsg { #[widget] impl Widget for HeaderBar { - fn model(data: (UnboundedSender, Sender, String, String)) -> HeaderBarModel { + fn model(data: (UnboundedSender, Sender)) -> HeaderBarModel { HeaderBarModel { - settings: relm::init::((data.0.clone(), data.2, data.3)) + settings: relm::init::(data.0.clone()) .expect("failed to create settings window"), rootwintx: data.1, } } + fn update(&mut self, msg: HeaderBarMsg) { match msg { HeaderBarMsg::OpenSettings => { diff --git a/src/gui/light_entry.rs b/src/gui/light_entry.rs index 1ce0424..5d26446 100644 --- a/src/gui/light_entry.rs +++ b/src/gui/light_entry.rs @@ -1,8 +1,9 @@ +use relm::Relm; use angular_units::Turns; use gtk::{prelude::*, Orientation}; use log::debug; use prisma::{Hsv, Rgb}; -use relm::Widget; +use relm::{Channel, Sender, Widget}; use relm_derive::{widget, Msg}; use rhue::{ api::{Light, LightArchetype}, @@ -14,6 +15,8 @@ use tokio::sync::mpsc::UnboundedSender; use crate::runtime::RuntimeMsg; pub struct LightEntryModel { + _channel: Channel, + tx: Sender, id: String, name: String, runtime: UnboundedSender, @@ -21,12 +24,18 @@ pub struct LightEntryModel { brightness: f64, color: Rc>>, icon: String, + updates_active: bool, } #[derive(Clone, Msg)] pub enum LightEntryMsg { Update(StateUpdate), PropertyChanged(EntryLightProperty), + /// Changing the values of the light's GUI elements before this message is + /// sent has no effect. This is to prevent light update requests being + /// sent to the bridge while their values are being set programatically + /// in the widget's initialization + ActivateUpdates, } #[derive(Clone, PartialEq, Eq)] @@ -37,8 +46,16 @@ pub enum EntryLightProperty { #[widget] impl Widget for LightEntry { - fn model(params: (String, Light, UnboundedSender)) -> LightEntryModel { + fn model( + relm: &Relm, + params: (String, Light, UnboundedSender), + ) -> LightEntryModel { + let stream = relm.stream().clone(); + let (ch, tx) = Channel::new(move |msg| stream.emit(msg)); + LightEntryModel { + _channel: ch, + tx, id: params.0, name: params.1.name, runtime: params.2, @@ -53,6 +70,7 @@ impl Widget for LightEntry { .into(), )), icon: resource_of_archetype(params.1.config.archetype), + updates_active: false, } } @@ -80,11 +98,13 @@ impl Widget for LightEntry { self.model.on = on; } - let _ = self - .model - .runtime - .send(RuntimeMsg::UpdateLight(self.model.id.clone(), upd)); - }, + if self.model.updates_active { + let _ = self + .model + .runtime + .send(RuntimeMsg::UpdateLight(self.model.id.clone(), upd)); + } + } LightEntryMsg::PropertyChanged(prop) => { debug!("updating light {}", &self.model.id); @@ -110,11 +130,15 @@ impl Widget for LightEntry { ..Default::default() }; - let _ = self - .model - .runtime - .send(RuntimeMsg::UpdateLight(self.model.id.clone(), upd)); - }, + if self.model.updates_active { + let _ = self + .model + .runtime + .send(RuntimeMsg::UpdateLight(self.model.id.clone(), upd)); + } + } + + LightEntryMsg::ActivateUpdates => self.model.updates_active = true, } } @@ -133,6 +157,12 @@ impl Widget for LightEntry { Inhibit(false) }); + + // we need to send this throught the channel, instead of just setting + // `self.model.updates_active`, since messages of the widget's stream + // are only handled once the view has been fully initialized. + // This would result in the updates being activated before the state is set. + let _ = self.model.tx.send(LightEntryMsg::ActivateUpdates); } view! { diff --git a/src/gui/mod.rs b/src/gui/mod.rs index bc1d5ec..1ef330e 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -52,7 +52,7 @@ impl Widget for Win { Model { _channel: ch, - headerbar: relm::init::((params.0.clone(), tx.clone(), params.1, params.2)) + headerbar: relm::init::((params.0.clone(), tx.clone())) .expect("header init"), runtime: params.0, tx, diff --git a/src/gui/settings.rs b/src/gui/settings.rs index 7318e24..3f1b121 100644 --- a/src/gui/settings.rs +++ b/src/gui/settings.rs @@ -23,28 +23,35 @@ pub enum SettingsMsg { Discover, Register, Save, - SetAddr(Option), - SetUsername(Result), + SetAddr(SettingsDataResponse), + SetUsername(SettingsDataResponse), +} + +#[derive(Clone)] +pub enum SettingsDataResponse { + FailedWithError(String), + FailedWithoutError, + Success(String), + Unset, } #[widget] impl Widget for Settings { - fn model( - relm: &Relm, - params: (UnboundedSender, String, String), - ) -> SettingsModel { + fn model(relm: &Relm, runtime: UnboundedSender) -> SettingsModel { let stream = relm.stream().clone(); let (ch, tx) = Channel::new(move |msg| stream.emit(msg)); + let _ = runtime.send(RuntimeMsg::RequestConfig(tx.clone())); + SettingsModel { _channel: ch, - runtime: params.0, + runtime, tx, status: String::new(), addr_spinning: false, username_spinning: false, - addr: params.1, - username: params.2, + addr: String::new(), + username: String::new(), } } @@ -81,25 +88,31 @@ impl Widget for Settings { } }, - SettingsMsg::SetAddr(addr) => { + SettingsMsg::SetAddr(res) => { self.model.addr_spinning = false; - if let Some(addr) = addr { - self.model.addr = addr; - } else { - self.model.status = "Couldn't find a Bridge.".to_string(); + match res { + SettingsDataResponse::FailedWithoutError => { + self.model.status = "Couldn't find a Bridge.".to_string() + }, + SettingsDataResponse::FailedWithError(e) => { + self.model.status = format!("Couldn't find a Bridge: {}", e) + }, + SettingsDataResponse::Success(addr) => self.model.addr = addr, + SettingsDataResponse::Unset => {}, } }, - SettingsMsg::SetUsername(username) => { + SettingsMsg::SetUsername(res) => { self.model.username_spinning = false; - match username { - Ok(u) => { - self.model.username = u; + match res { + SettingsDataResponse::FailedWithoutError => { + self.model.status = "Failed to register.".to_string() }, - - Err(e) => { - self.model.status = format!("Failed to register at bridge: {}", e); + SettingsDataResponse::FailedWithError(e) => { + self.model.status = format!("Failed to register: {}", e) }, + SettingsDataResponse::Success(username) => self.model.username = username, + SettingsDataResponse::Unset => {}, } }, } diff --git a/src/runtime.rs b/src/runtime.rs index ac8b244..ea3ee99 100644 --- a/src/runtime.rs +++ b/src/runtime.rs @@ -4,7 +4,10 @@ use tokio::sync::{mpsc, RwLock}; use crate::{ config::{Config, ConfigData}, - gui::{settings::SettingsMsg, Msg}, + gui::{ + settings::{SettingsDataResponse, SettingsMsg}, + Msg, + }, }; use log::{error, info}; use miette::{miette, Context, IntoDiagnostic}; @@ -123,20 +126,27 @@ impl Runtime { RuntimeMsg::DiscoverBridge(tx) => { let addr = rhue::discover_bridge(Arc::clone(&http), Duration::from_secs(5)).await?; - tx.send(SettingsMsg::SetAddr(addr.map(|u| u.to_string()))) - .into_diagnostic()?; + tx.send(SettingsMsg::SetAddr(match addr { + Some(a) => SettingsDataResponse::Success(a.to_string()), + None => SettingsDataResponse::FailedWithoutError, + })) + .into_diagnostic()?; }, RuntimeMsg::Register(url, tx) => { match Bridge::register(http, "GUE", url).await?.to_result() { Ok(b) => { let username = b.username.clone(); *bridge.write().await = Some(b); - tx.send(SettingsMsg::SetUsername(Ok(username))) - .into_diagnostic()?; + tx.send(SettingsMsg::SetUsername(SettingsDataResponse::Success( + username, + ))) + .into_diagnostic()?; }, Err(e) => { - tx.send(SettingsMsg::SetUsername(Err(e.description))) - .into_diagnostic()?; + tx.send(SettingsMsg::SetUsername( + SettingsDataResponse::FailedWithError(e.description), + )) + .into_diagnostic()?; }, } }, @@ -146,6 +156,22 @@ impl Runtime { bridge.update_light(&id, upd).await?; } }, + + RuntimeMsg::RequestConfig(tx) => { + let cfg = config.read().await; + + tx.send(SettingsMsg::SetAddr(match &cfg.data.bridge_addr { + Some(a) => SettingsDataResponse::Success(a.to_string()), + None => SettingsDataResponse::Unset, + })) + .into_diagnostic()?; + + tx.send(SettingsMsg::SetUsername(match &cfg.data.bridge_username { + Some(a) => SettingsDataResponse::Success(a.clone()), + None => SettingsDataResponse::Unset, + })) + .into_diagnostic()?; + }, } Ok(false) @@ -159,4 +185,5 @@ pub enum RuntimeMsg { DiscoverBridge(Sender), Register(Url, Sender), UpdateLight(String, StateUpdate), + RequestConfig(Sender), }