More refactoring
This commit is contained in:
parent
dced5cba03
commit
19a039f98d
3
Linux/.vscode/settings.json
vendored
3
Linux/.vscode/settings.json
vendored
@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"nixEnvSelector.nixFile": "${workspaceFolder}/shell.nix",
|
"nixEnvSelector.nixFile": "${workspaceFolder}/shell.nix",
|
||||||
"files.trimTrailingWhitespace": true,
|
"files.trimTrailingWhitespace": true,
|
||||||
"rust-analyzer.imports.granularity.group": "item"
|
"rust-analyzer.imports.granularity.group": "item",
|
||||||
|
"rust-analyzer.check.command": "clippy"
|
||||||
}
|
}
|
671
Linux/Cargo.lock
generated
671
Linux/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -1,22 +1,6 @@
|
|||||||
[package]
|
[workspace]
|
||||||
name = "MassiveKnob"
|
resolver = "2"
|
||||||
version = "0.1.0"
|
members = [
|
||||||
edition = "2021"
|
"backend",
|
||||||
build = "build.rs"
|
"ui-gtk"
|
||||||
|
]
|
||||||
[dependencies]
|
|
||||||
anyhow = "1.0.86"
|
|
||||||
env_logger = "0.11.3"
|
|
||||||
log = "0.4.21"
|
|
||||||
platform-dirs = "0.3.0"
|
|
||||||
regex = "1.10.5"
|
|
||||||
relm4 = "0.8.1"
|
|
||||||
relm4-icons = "0.8.3"
|
|
||||||
rust-i18n = "3.0.1"
|
|
||||||
serde = { version = "1.0.203", features = ["derive"] }
|
|
||||||
serde_json = "1.0.117"
|
|
||||||
serialport = { version = "4.4.0", default-features = false }
|
|
||||||
tracker = "0.2.2"
|
|
||||||
|
|
||||||
[dependencies.min-rs]
|
|
||||||
git = "https://github.com/MvRens/min-rs.git"
|
|
||||||
|
23
Linux/backend/Cargo.toml
Normal file
23
Linux/backend/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
[package]
|
||||||
|
name = "massiveknob_backend"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
anyhow = "1.0.86"
|
||||||
|
log = "0.4.21"
|
||||||
|
platform-dirs = "0.3.0"
|
||||||
|
regex = "1.10.5"
|
||||||
|
rust-i18n = "3.0.1"
|
||||||
|
serde = { version = "1.0.203", features = ["derive"] }
|
||||||
|
serde_json = "1.0.117"
|
||||||
|
serialport = { version = "4.4.0", default-features = false }
|
||||||
|
tracker = "0.2.2"
|
||||||
|
|
||||||
|
[dependencies.uuid]
|
||||||
|
version = "1.10.0"
|
||||||
|
features = [ "v4", "fast-rng", "macro-diagnostics" ]
|
||||||
|
|
||||||
|
[dependencies.min-rs]
|
||||||
|
git = "https://github.com/MvRens/min-rs.git"
|
9
Linux/backend/locales/actions/pipewire.yaml
Normal file
9
Linux/backend/locales/actions/pipewire.yaml
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
_version: 2
|
||||||
|
|
||||||
|
actions:
|
||||||
|
pipewire:
|
||||||
|
group_name:
|
||||||
|
en: Pipewire
|
||||||
|
set_volume:
|
||||||
|
name:
|
||||||
|
en: Set volume
|
6
Linux/backend/locales/devices/serial_min.yaml
Normal file
6
Linux/backend/locales/devices/serial_min.yaml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
_version: 2
|
||||||
|
|
||||||
|
devices:
|
||||||
|
serial_min:
|
||||||
|
name:
|
||||||
|
en: Serial device using MIN protocol
|
42
Linux/backend/src/actions/mod.rs
Normal file
42
Linux/backend/src/actions/mod.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
pub mod pipewire;
|
||||||
|
|
||||||
|
|
||||||
|
use crate::util::unique_id::UniqueId;
|
||||||
|
|
||||||
|
|
||||||
|
pub enum ActionGroup
|
||||||
|
{
|
||||||
|
Pipewire(pipewire::PipewireActionGroupInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub trait ActionGroupInfo
|
||||||
|
{
|
||||||
|
/// The unique ID of the group. This should remain stable across releases for
|
||||||
|
/// the purpose of storing it in the user's configuration.
|
||||||
|
fn unique_id(&self) -> UniqueId;
|
||||||
|
|
||||||
|
/// The name of the group for display purposes.
|
||||||
|
fn name(&self) -> String
|
||||||
|
{
|
||||||
|
t!(format!("actions.{}.name", self.unique_id().as_str()).as_str()).to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub trait ActionInfo
|
||||||
|
{
|
||||||
|
/// The unique ID of the item. This should remain stable across releases for
|
||||||
|
/// the purpose of storing it in the user's configuration.
|
||||||
|
fn unique_id(&self) -> UniqueId;
|
||||||
|
|
||||||
|
/// The unique ID of the group this action belongs to.
|
||||||
|
fn group_id(&self) -> UniqueId;
|
||||||
|
|
||||||
|
/// The name of the item for display purposes.
|
||||||
|
fn name(&self) -> String
|
||||||
|
{
|
||||||
|
t!(format!("actions.{}.{}.name", self.group_id().as_str(), self.unique_id().as_str()).as_str()).to_string()
|
||||||
|
}
|
||||||
|
}
|
23
Linux/backend/src/actions/pipewire/mod.rs
Normal file
23
Linux/backend/src/actions/pipewire/mod.rs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
use crate::util::unique_id::UniqueId;
|
||||||
|
|
||||||
|
use super::ActionGroupInfo;
|
||||||
|
|
||||||
|
pub mod set_volume;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub enum PipewireAction
|
||||||
|
{
|
||||||
|
SetVolume(set_volume::PipewireSetVolumeActionInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct PipewireActionGroupInfo
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl ActionGroupInfo for PipewireActionGroupInfo
|
||||||
|
{
|
||||||
|
fn unique_id(&self) -> UniqueId { UniqueId::from("pipewire") }
|
||||||
|
}
|
13
Linux/backend/src/actions/pipewire/set_volume.rs
Normal file
13
Linux/backend/src/actions/pipewire/set_volume.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
use crate::actions::ActionInfo;
|
||||||
|
use crate::util::unique_id::UniqueId;
|
||||||
|
|
||||||
|
pub struct PipewireSetVolumeActionInfo
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl ActionInfo for PipewireSetVolumeActionInfo
|
||||||
|
{
|
||||||
|
fn unique_id(&self) -> crate::util::unique_id::UniqueId { UniqueId::from("set_volume") }
|
||||||
|
fn group_id(&self) -> crate::util::unique_id::UniqueId { UniqueId::from("pipewire") }
|
||||||
|
}
|
@ -19,6 +19,7 @@ pub struct ConfigManager
|
|||||||
|
|
||||||
impl ConfigManager
|
impl ConfigManager
|
||||||
{
|
{
|
||||||
|
#![allow(clippy::new_without_default)]
|
||||||
pub fn new() -> Self
|
pub fn new() -> Self
|
||||||
{
|
{
|
||||||
let appdirs = AppDirs::new(Some("massiveknob"), false).unwrap();
|
let appdirs = AppDirs::new(Some("massiveknob"), false).unwrap();
|
||||||
@ -37,7 +38,7 @@ impl ConfigManager
|
|||||||
{
|
{
|
||||||
return OptionResult::None;
|
return OptionResult::None;
|
||||||
}
|
}
|
||||||
|
|
||||||
match std::fs::File::open(path)
|
match std::fs::File::open(path)
|
||||||
{
|
{
|
||||||
Ok(v) => OptionResult::Some(v),
|
Ok(v) => OptionResult::Some(v),
|
||||||
@ -62,7 +63,7 @@ impl ConfigManager
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::fs::File::create(path)
|
std::fs::File::create(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
18
Linux/backend/src/devices/emulator.rs
Normal file
18
Linux/backend/src/devices/emulator.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use super::DeviceContext;
|
||||||
|
|
||||||
|
pub struct EmulatorDevice
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl EmulatorDevice
|
||||||
|
{
|
||||||
|
pub fn new(_context: DeviceContext, _instance_id: Uuid) -> Self
|
||||||
|
{
|
||||||
|
Self
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
87
Linux/backend/src/devices/mod.rs
Normal file
87
Linux/backend/src/devices/mod.rs
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
pub mod emulator;
|
||||||
|
pub mod serial_min;
|
||||||
|
|
||||||
|
|
||||||
|
use emulator::EmulatorDevice;
|
||||||
|
use serial_min::SerialMinDevice;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::util::unique_id::UniqueId;
|
||||||
|
|
||||||
|
// TODO use a procedural macro to build the registry based on attributes in the Device enum?
|
||||||
|
|
||||||
|
pub enum Device
|
||||||
|
{
|
||||||
|
Emulator(emulator::EmulatorDevice),
|
||||||
|
SerialMin(serial_min::SerialMinDevice)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct DeviceInfo
|
||||||
|
{
|
||||||
|
/// The unique ID of the item. This should remain stable across releases for
|
||||||
|
/// the purpose of storing it in the user's configuration.
|
||||||
|
pub unique_id: UniqueId,
|
||||||
|
|
||||||
|
pub factory: fn(DeviceContext, Uuid) -> Device
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct DeviceRegistry
|
||||||
|
{
|
||||||
|
info: Vec<DeviceInfo>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct DeviceContext
|
||||||
|
{
|
||||||
|
// Arc<ConfigManager>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl DeviceRegistry
|
||||||
|
{
|
||||||
|
#![allow(clippy::new_without_default)]
|
||||||
|
pub fn new() -> Self
|
||||||
|
{
|
||||||
|
Self
|
||||||
|
{
|
||||||
|
info: vec!(
|
||||||
|
DeviceInfo
|
||||||
|
{
|
||||||
|
unique_id: UniqueId::from("emulator"),
|
||||||
|
factory: |context, instance_id| Device::Emulator(EmulatorDevice::new(context, instance_id))
|
||||||
|
},
|
||||||
|
|
||||||
|
DeviceInfo
|
||||||
|
{
|
||||||
|
unique_id: UniqueId::from("serial_min"),
|
||||||
|
factory: |context, instance_id| Device::SerialMin(SerialMinDevice::new(context, instance_id))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn iter(&self) -> impl Iterator<Item = &DeviceInfo>
|
||||||
|
{
|
||||||
|
self.info.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn by_id(&self, id: &UniqueId) -> Option<&DeviceInfo>
|
||||||
|
{
|
||||||
|
self.info.iter().find(|v| &v.unique_id == id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
impl DeviceInfo
|
||||||
|
{
|
||||||
|
/// The name of the item for display purposes.
|
||||||
|
pub fn name(&self) -> String
|
||||||
|
{
|
||||||
|
t!(format!("devices.{}.name", self.unique_id.as_str()).as_str()).to_string()
|
||||||
|
}
|
||||||
|
}
|
19
Linux/backend/src/devices/serial_min.rs
Normal file
19
Linux/backend/src/devices/serial_min.rs
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use super::DeviceContext;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct SerialMinDevice
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl SerialMinDevice
|
||||||
|
{
|
||||||
|
pub fn new(_context: DeviceContext, _instance_id: Uuid) -> Self
|
||||||
|
{
|
||||||
|
Self
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
Linux/backend/src/lib.rs
Normal file
12
Linux/backend/src/lib.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate rust_i18n;
|
||||||
|
|
||||||
|
i18n!("locales");
|
||||||
|
|
||||||
|
|
||||||
|
pub mod util;
|
||||||
|
pub mod devices;
|
||||||
|
pub mod actions;
|
||||||
|
|
||||||
|
pub mod config;
|
||||||
|
pub mod orchestrator;
|
@ -129,12 +129,16 @@ impl MainWindow
|
|||||||
{
|
{
|
||||||
if initial
|
if initial
|
||||||
{
|
{
|
||||||
self.orchestrator.with_active_device(|device_instance, cookie| { widget = device_instance.create_settings_widget(cookie) });
|
if let Some(active_device) = self.orchestrator.active_device()
|
||||||
|
{
|
||||||
|
active_device
|
||||||
|
}
|
||||||
|
self.orchestrator.with_active_device(|device_instance| { widget = device_instance.create_settings_widget() });
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
let device_id = self.devices_sorted[index].unique_id.clone();
|
let device_id = self.devices_sorted[index].unique_id.clone();
|
||||||
self.orchestrator.set_active_device_id(&device_id, |device_instance, cookie| { widget = device_instance.create_settings_widget(cookie) });
|
self.orchestrator.set_active_device_id(&device_id, |device_instance| { widget = device_instance.create_settings_widget() });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,13 +1,14 @@
|
|||||||
use std::borrow::{Borrow, BorrowMut};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::actions;
|
use uuid::Uuid;
|
||||||
//use crate::actions::ActionRegistryItem;
|
|
||||||
use crate::config::json::JsonConfigManager;
|
use crate::config::json::JsonConfigManager;
|
||||||
use crate::config::{ConfigManager, ConfigName};
|
use crate::config::ConfigName;
|
||||||
use crate::devices::{self, Device};
|
use crate::config::ConfigManager;
|
||||||
use crate::devices::DeviceRegistryItem;
|
use crate::devices::DeviceContext;
|
||||||
use crate::registry::Registry;
|
use crate::devices::DeviceInfo;
|
||||||
|
use crate::devices::DeviceRegistry;
|
||||||
|
use crate::devices::Device;
|
||||||
use crate::util::unique_id::UniqueId;
|
use crate::util::unique_id::UniqueId;
|
||||||
|
|
||||||
|
|
||||||
@ -19,44 +20,30 @@ pub struct Orchestrator
|
|||||||
config_manager: ConfigManager,
|
config_manager: ConfigManager,
|
||||||
settings_name: ConfigName,
|
settings_name: ConfigName,
|
||||||
|
|
||||||
device_registry: Registry<DeviceRegistryItem>,
|
device_registry: DeviceRegistry,
|
||||||
//action_registry: Registry<ActionRegistryItem>,
|
|
||||||
|
|
||||||
// current_device_instance: Option<Box<dyn Device>>
|
|
||||||
active_device: Option<ActiveDevice>
|
active_device: Option<ActiveDevice>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl Orchestrator
|
impl Orchestrator
|
||||||
{
|
{
|
||||||
|
#![allow(clippy::new_without_default)]
|
||||||
pub fn new() -> Self
|
pub fn new() -> Self
|
||||||
{
|
{
|
||||||
let config_manager = ConfigManager::new();
|
let config_manager = ConfigManager::new();
|
||||||
let settings_name = ConfigName::new("settings");
|
let settings_name = ConfigName::new("settings");
|
||||||
let settings = match config_manager.read_json(&settings_name).expect("Error reading settings")
|
let settings = config_manager.read_json(&settings_name).expect("Error reading settings").unwrap_or_default();
|
||||||
{
|
|
||||||
None => settings::Settings::default(),
|
|
||||||
Some(v) => v
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
let mut device_registry = Registry::new();
|
|
||||||
let mut action_registry = Registry::new();
|
|
||||||
|
|
||||||
devices::register(&mut device_registry);
|
|
||||||
actions::register(&mut action_registry);
|
|
||||||
|
|
||||||
let mut instance = Self
|
let mut instance = Self
|
||||||
{
|
{
|
||||||
config_manager,
|
config_manager,
|
||||||
settings_name,
|
settings_name,
|
||||||
|
|
||||||
device_registry,
|
device_registry: DeviceRegistry::new(),
|
||||||
//action_registry,
|
|
||||||
|
|
||||||
//settings,
|
//settings,
|
||||||
|
|
||||||
|
|
||||||
active_device: None
|
active_device: None
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -75,7 +62,7 @@ impl Orchestrator
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub fn devices(&self) -> impl Iterator<Item = &DeviceRegistryItem>
|
pub fn devices(&self) -> impl Iterator<Item = &DeviceInfo>
|
||||||
{
|
{
|
||||||
self.device_registry.iter()
|
self.device_registry.iter()
|
||||||
}
|
}
|
||||||
@ -87,38 +74,31 @@ impl Orchestrator
|
|||||||
Some(active_device.id.clone())
|
Some(active_device.id.clone())
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
|
|
||||||
|
pub fn active_device(&self) -> Option<DeviceReference>
|
||||||
pub fn current_device(&self) -> Option<&DeviceRegistryItem>
|
|
||||||
{
|
{
|
||||||
let Some(device_id) = self.current_device_id() else { return None };
|
let Some(active_device) = &self.active_device else { return None };
|
||||||
self.device_registry.by_id(device_id)
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
pub fn with_active_device<F>(&self, callback: F) where F: FnOnce(&dyn Device)
|
|
||||||
{
|
|
||||||
let Some(active_device) = self.active_device else { return };
|
|
||||||
let instance = active_device.instance.clone();
|
let instance = active_device.instance.clone();
|
||||||
|
|
||||||
callback(instance.as_ref().as_ref());
|
Some(instance)
|
||||||
|
|
||||||
self.active_device.as_ref().map(|device| callback(&*device.instance.clone()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub fn set_active_device_id(&mut self, id: &UniqueId) -> DeviceReference
|
||||||
pub fn set_active_device_id<F>(&mut self, id: &UniqueId, on_changed: F) where F: FnOnce(&dyn Device)
|
|
||||||
{
|
{
|
||||||
let id = Some(id.clone());
|
if let Some(active_device) = &self.active_device
|
||||||
if id == self.active_device_id() { return }
|
{
|
||||||
|
if *id == active_device.id
|
||||||
|
{
|
||||||
|
return active_device.instance.clone();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.set_active_device(id);
|
|
||||||
|
let active_device = self.set_active_device(Some(id.clone())).unwrap_or_else(|| panic!("Invalid device ID: {}", id.as_str()));
|
||||||
self.store_settings();
|
self.store_settings();
|
||||||
|
|
||||||
self.with_active_device(on_changed);
|
active_device
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -127,11 +107,7 @@ impl Orchestrator
|
|||||||
{
|
{
|
||||||
let settings = settings::Settings
|
let settings = settings::Settings
|
||||||
{
|
{
|
||||||
device_id: match &self.active_device
|
device_id: self.active_device.as_ref().map(|v| v.id.clone().into())
|
||||||
{
|
|
||||||
None => None,
|
|
||||||
Some(v) => Some(v.id.clone().into())
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(e) = self.config_manager.write_json(&self.settings_name, &settings)
|
if let Err(e) = self.config_manager.write_json(&self.settings_name, &settings)
|
||||||
@ -142,9 +118,9 @@ impl Orchestrator
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
fn set_active_device(&mut self, id: Option<UniqueId>)
|
fn set_active_device(&mut self, id: Option<UniqueId>) -> Option<DeviceReference>
|
||||||
{
|
{
|
||||||
self.active_device = match id
|
let new_device = match id
|
||||||
{
|
{
|
||||||
None => None,
|
None => None,
|
||||||
Some(v) =>
|
Some(v) =>
|
||||||
@ -153,14 +129,25 @@ impl Orchestrator
|
|||||||
None => None,
|
None => None,
|
||||||
Some(d) =>
|
Some(d) =>
|
||||||
{
|
{
|
||||||
|
let context = DeviceContext
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
let instance_id = Uuid::new_v4();
|
||||||
|
|
||||||
Some(ActiveDevice
|
Some(ActiveDevice
|
||||||
{
|
{
|
||||||
id: v.clone(),
|
id: v.clone(),
|
||||||
instance: Arc::new((d.factory)())
|
instance: Arc::new((d.factory)(context, instance_id))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let result = new_device.as_ref().map(|v| v.instance.clone());
|
||||||
|
|
||||||
|
self.active_device = new_device;
|
||||||
|
result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,8 +162,11 @@ impl Drop for Orchestrator
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub type DeviceReference = Arc<Device>;
|
||||||
|
|
||||||
|
|
||||||
struct ActiveDevice
|
struct ActiveDevice
|
||||||
{
|
{
|
||||||
id: UniqueId,
|
id: UniqueId,
|
||||||
instance: Arc<Box<dyn Device>>
|
instance: DeviceReference
|
||||||
}
|
}
|
7
Linux/backend/src/orchestrator/settings.rs
Normal file
7
Linux/backend/src/orchestrator/settings.rs
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
#[derive(Default, Serialize, Deserialize)]
|
||||||
|
pub struct Settings
|
||||||
|
{
|
||||||
|
pub device_id: Option<String>
|
||||||
|
}
|
@ -68,11 +68,11 @@ impl<T: ValidatedStringPattern> From<String> for ValidatedString<T>
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<T: ValidatedStringPattern> Into<String> for ValidatedString<T>
|
impl<T: ValidatedStringPattern> From<ValidatedString<T>> for String
|
||||||
{
|
{
|
||||||
fn into(self) -> String
|
fn from(val: ValidatedString<T>) -> Self
|
||||||
{
|
{
|
||||||
self.inner.clone()
|
val.inner.clone()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,37 +0,0 @@
|
|||||||
pub mod pipewire;
|
|
||||||
|
|
||||||
use crate::registry::Registry;
|
|
||||||
use crate::registry::RegistryItem;
|
|
||||||
use crate::util::unique_id::UniqueId;
|
|
||||||
|
|
||||||
|
|
||||||
pub struct ActionRegistryItem
|
|
||||||
{
|
|
||||||
pub unique_id: UniqueId
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub trait Action
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl RegistryItem for ActionRegistryItem
|
|
||||||
{
|
|
||||||
fn unique_id(&self) -> UniqueId
|
|
||||||
{
|
|
||||||
self.unique_id.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name(&self) -> String
|
|
||||||
{
|
|
||||||
t!(format!("actions.{}.name", self.unique_id.as_str()).as_str()).to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub fn register(registry: &mut Registry<ActionRegistryItem>)
|
|
||||||
{
|
|
||||||
pipewire::register(registry);
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
use crate::registry::Registry;
|
|
||||||
use crate::util::unique_id::UniqueId;
|
|
||||||
use super::ActionRegistryItem;
|
|
||||||
|
|
||||||
pub mod set_volume;
|
|
||||||
|
|
||||||
|
|
||||||
pub fn register(registry: &mut Registry<ActionRegistryItem>)
|
|
||||||
{
|
|
||||||
registry.register(ActionRegistryItem
|
|
||||||
{
|
|
||||||
unique_id: UniqueId::from("pipewire.set_volume")
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,48 +0,0 @@
|
|||||||
use gtk::prelude::*;
|
|
||||||
use relm4::prelude::*;
|
|
||||||
|
|
||||||
pub struct EmulatorWindow
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum EmulatorWindowMessage
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[relm4::component(pub)]
|
|
||||||
impl SimpleComponent for EmulatorWindow
|
|
||||||
{
|
|
||||||
type Init = ();
|
|
||||||
type Input = EmulatorWindowMessage;
|
|
||||||
type Output = ();
|
|
||||||
|
|
||||||
view!
|
|
||||||
{
|
|
||||||
gtk::Window
|
|
||||||
{
|
|
||||||
set_title: Some(&t!("emulatorwindow.title")),
|
|
||||||
set_default_size: (300, 500)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn init(_data: Self::Init, root: Self::Root, _sender: ComponentSender<Self>, ) -> ComponentParts<Self>
|
|
||||||
{
|
|
||||||
let model = EmulatorWindow {};
|
|
||||||
let widgets = view_output!();
|
|
||||||
|
|
||||||
root.set_visible(true);
|
|
||||||
ComponentParts { model, widgets }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>)
|
|
||||||
{
|
|
||||||
match msg
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
use emulatorwindow::EmulatorWindow;
|
|
||||||
use relm4::component::Connector;
|
|
||||||
use relm4::Component;
|
|
||||||
use relm4::gtk::prelude::*;
|
|
||||||
use relm4::ComponentController;
|
|
||||||
|
|
||||||
use crate::registry::Registry;
|
|
||||||
use crate::util::unique_id::UniqueId;
|
|
||||||
use super::Device;
|
|
||||||
use super::DeviceRegistryItem;
|
|
||||||
|
|
||||||
|
|
||||||
pub mod emulatorwindow;
|
|
||||||
|
|
||||||
|
|
||||||
pub struct EmulatorWindowDevice
|
|
||||||
{
|
|
||||||
window: Connector<EmulatorWindow>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl EmulatorWindowDevice
|
|
||||||
{
|
|
||||||
fn new() -> Self
|
|
||||||
{
|
|
||||||
let builder = EmulatorWindow::builder();
|
|
||||||
let window = builder.launch({});
|
|
||||||
|
|
||||||
EmulatorWindowDevice
|
|
||||||
{
|
|
||||||
window
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl Device for EmulatorWindowDevice
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl Drop for EmulatorWindowDevice
|
|
||||||
{
|
|
||||||
fn drop(&mut self)
|
|
||||||
{
|
|
||||||
self.window.widget().close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub fn register(registry: &mut Registry<DeviceRegistryItem>)
|
|
||||||
{
|
|
||||||
registry.register(DeviceRegistryItem {
|
|
||||||
unique_id: UniqueId::from("emulator"),
|
|
||||||
factory: || Box::new(EmulatorWindowDevice::new()),
|
|
||||||
settings_widget_factory: || None
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
pub mod emulator;
|
|
||||||
pub mod serial_min;
|
|
||||||
|
|
||||||
|
|
||||||
use crate::registry::Registry;
|
|
||||||
use crate::registry::RegistryItem;
|
|
||||||
use crate::ui::EmbeddedWidgetConnector;
|
|
||||||
use crate::util::unique_id::UniqueId;
|
|
||||||
|
|
||||||
|
|
||||||
pub struct DeviceRegistryItem
|
|
||||||
{
|
|
||||||
pub unique_id: UniqueId,
|
|
||||||
pub factory: fn() -> Box<dyn Device>,
|
|
||||||
pub settings_widget_factory: fn() -> Option<Box<dyn EmbeddedWidgetConnector>>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub trait Device
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl RegistryItem for DeviceRegistryItem
|
|
||||||
{
|
|
||||||
fn unique_id(&self) -> UniqueId
|
|
||||||
{
|
|
||||||
self.unique_id.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn name(&self) -> String
|
|
||||||
{
|
|
||||||
t!(format!("devices.{}.name", self.unique_id.as_str()).as_str()).to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub fn register(registry: &mut Registry<DeviceRegistryItem>)
|
|
||||||
{
|
|
||||||
emulator::register(registry);
|
|
||||||
serial_min::register(registry);
|
|
||||||
}
|
|
@ -1,38 +0,0 @@
|
|||||||
use relm4::Component;
|
|
||||||
use settingswidget::SerialMinSettingsInit;
|
|
||||||
use settingswidget::SerialMinSettingsWidget;
|
|
||||||
|
|
||||||
use crate::registry::Registry;
|
|
||||||
use crate::util::unique_id::UniqueId;
|
|
||||||
use super::Device;
|
|
||||||
use super::DeviceRegistryItem;
|
|
||||||
|
|
||||||
|
|
||||||
pub mod settingswidget;
|
|
||||||
|
|
||||||
|
|
||||||
pub struct SerialMinDevice
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl Device for SerialMinDevice
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub fn register(registry: &mut Registry<DeviceRegistryItem>)
|
|
||||||
{
|
|
||||||
registry.register(DeviceRegistryItem {
|
|
||||||
unique_id: UniqueId::from("serial_min"),
|
|
||||||
factory: || Box::new(SerialMinDevice {}),
|
|
||||||
settings_widget_factory: ||
|
|
||||||
{
|
|
||||||
let builder = SerialMinSettingsWidget::builder();
|
|
||||||
Some(Box::new(builder.launch(SerialMinSettingsInit
|
|
||||||
{
|
|
||||||
})))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
use env_logger::Env;
|
|
||||||
use relm4::prelude::*;
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate rust_i18n;
|
|
||||||
|
|
||||||
i18n!("locales");
|
|
||||||
|
|
||||||
|
|
||||||
pub mod util;
|
|
||||||
pub mod ui;
|
|
||||||
pub mod registry;
|
|
||||||
pub mod devices;
|
|
||||||
pub mod actions;
|
|
||||||
|
|
||||||
pub mod config;
|
|
||||||
pub mod orchestrator;
|
|
||||||
pub mod mainwindow;
|
|
||||||
|
|
||||||
fn main()
|
|
||||||
{
|
|
||||||
env_logger::Builder::from_env(Env::default().default_filter_or("info"))
|
|
||||||
// .format_timestamp(None)
|
|
||||||
.init();
|
|
||||||
|
|
||||||
relm4_icons::initialize_icons();
|
|
||||||
|
|
||||||
let app = RelmApp::new("com.github.mvrens.massiveknob");
|
|
||||||
app.run::<mainwindow::MainWindow>(());
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
use serde::{Serialize, Deserialize};
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct Settings
|
|
||||||
{
|
|
||||||
pub device_id: Option<String>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
impl Default for Settings
|
|
||||||
{
|
|
||||||
fn default() -> Self
|
|
||||||
{
|
|
||||||
Self
|
|
||||||
{
|
|
||||||
device_id: None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
use std::collections::HashMap;
|
|
||||||
use log;
|
|
||||||
|
|
||||||
use crate::util::unique_id::UniqueId;
|
|
||||||
|
|
||||||
|
|
||||||
pub trait RegistryItem
|
|
||||||
{
|
|
||||||
/// The unique ID of the item. This should remain stable across releases for
|
|
||||||
/// the purpose of storing it in the user's configuration.
|
|
||||||
fn unique_id(&self) -> UniqueId;
|
|
||||||
|
|
||||||
/// The name of the item for display purposes.
|
|
||||||
fn name(&self) -> String;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
pub struct Registry<T> where T: RegistryItem
|
|
||||||
{
|
|
||||||
items: HashMap<String, T>
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
impl<T> Registry<T> where T: RegistryItem
|
|
||||||
{
|
|
||||||
pub fn new() -> Self
|
|
||||||
{
|
|
||||||
Self
|
|
||||||
{
|
|
||||||
items: HashMap::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub fn register(&mut self, device: T)
|
|
||||||
{
|
|
||||||
let device_id = device.unique_id();
|
|
||||||
|
|
||||||
log::debug!("Registered device: [{}] {}", device_id.as_str(), device.name());
|
|
||||||
self.items.insert(String::from(device_id.as_str()), device);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &T>
|
|
||||||
{
|
|
||||||
self.items.values()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub fn by_id(&self, id: &UniqueId) -> Option<&T>
|
|
||||||
{
|
|
||||||
self.items.get(id.as_str())
|
|
||||||
}
|
|
||||||
}
|
|
16
Linux/ui-gtk/Cargo.toml
Normal file
16
Linux/ui-gtk/Cargo.toml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "massiveknob_ui_gtk"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
build = "build.rs"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
env_logger = "0.11.3"
|
||||||
|
gtk = { version = "0.9.0", package = "gtk4", features = ["v4_14"] }
|
||||||
|
log = "0.4.21"
|
||||||
|
rust-i18n = "3.0.1"
|
||||||
|
serialport = { version = "4.4.0", default-features = false }
|
||||||
|
tracker = "0.2.2"
|
||||||
|
|
||||||
|
[dependencies.massiveknob_backend]
|
||||||
|
path = "../backend"
|
4
Linux/ui-gtk/build.rs
Normal file
4
Linux/ui-gtk/build.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
fn main()
|
||||||
|
{
|
||||||
|
println!("cargo:rerun-if-changed=locales");
|
||||||
|
}
|
2
Linux/ui-gtk/icons.toml
Normal file
2
Linux/ui-gtk/icons.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
base_resource_path = "/com/github/mvrens/massiveknob/"
|
||||||
|
icons = []
|
6
Linux/ui-gtk/locales/devices/emulator.yaml
Normal file
6
Linux/ui-gtk/locales/devices/emulator.yaml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
_version: 2
|
||||||
|
|
||||||
|
devices:
|
||||||
|
emulator:
|
||||||
|
name:
|
||||||
|
en: Emulator
|
101
Linux/ui-gtk/src/devices/emulator_settings_ui.rs
Normal file
101
Linux/ui-gtk/src/devices/emulator_settings_ui.rs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
use massiveknob_backend::orchestrator::DeviceReference;
|
||||||
|
|
||||||
|
use crate::ui::uicomponent::UiComponent;
|
||||||
|
use crate::ui::uicomponent::UiComponentState;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct EmulatorSettingsUiInit
|
||||||
|
{
|
||||||
|
pub device: DeviceReference
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub struct EmulatorSettingsUi
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct EmulatorSettingsUiWidgets
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl UiComponent for EmulatorSettingsUi
|
||||||
|
{
|
||||||
|
type Root = gtk::Box;
|
||||||
|
type Widgets = EmulatorSettingsUiWidgets;
|
||||||
|
type Init = EmulatorSettingsUiInit;
|
||||||
|
type State = Self;
|
||||||
|
|
||||||
|
|
||||||
|
fn build_root(_init: &Self::Init) -> Self::Root
|
||||||
|
{
|
||||||
|
gtk::Box::builder()
|
||||||
|
.hexpand(true)
|
||||||
|
.orientation(gtk::Orientation::Vertical)
|
||||||
|
.spacing(8)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_widgets(_root: &Self::Root, _init: &Self::Init) -> Self::Widgets
|
||||||
|
{
|
||||||
|
Self::Widgets
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init(_root: &Self::Root, _state: &std::rc::Rc<std::cell::RefCell<Self::State>>)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl UiComponentState<EmulatorSettingsUi> for EmulatorSettingsUi
|
||||||
|
{
|
||||||
|
fn new(_init: (), _widgets: EmulatorSettingsUiWidgets) -> Self
|
||||||
|
{
|
||||||
|
Self
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
#[relm4::component(pub)]
|
||||||
|
impl SimpleComponent for EmulatorWindow
|
||||||
|
{
|
||||||
|
type Init = ();
|
||||||
|
type Input = EmulatorWindowMessage;
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
view!
|
||||||
|
{
|
||||||
|
gtk::Window
|
||||||
|
{
|
||||||
|
set_title: Some(&t!("emulatorwindow.title")),
|
||||||
|
set_default_size: (300, 500)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn init(_data: Self::Init, root: Self::Root, _sender: ComponentSender<Self>, ) -> ComponentParts<Self>
|
||||||
|
{
|
||||||
|
let model = EmulatorWindow {};
|
||||||
|
let widgets = view_output!();
|
||||||
|
|
||||||
|
root.set_visible(true);
|
||||||
|
ComponentParts { model, widgets }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>)
|
||||||
|
{
|
||||||
|
match msg
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
30
Linux/ui-gtk/src/devices/mod.rs
Normal file
30
Linux/ui-gtk/src/devices/mod.rs
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
use emulator_settings_ui::EmulatorSettingsUi;
|
||||||
|
use emulator_settings_ui::EmulatorSettingsUiInit;
|
||||||
|
use massiveknob_backend::devices::Device;
|
||||||
|
use massiveknob_backend::orchestrator::DeviceReference;
|
||||||
|
use serial_min_settings_ui::SerialMinSettingsUi;
|
||||||
|
use serial_min_settings_ui::SerialMinSettingsUiInit;
|
||||||
|
|
||||||
|
use crate::ui::uicomponent::UiComponent;
|
||||||
|
use crate::ui::uicomponent::UiComponentConnectorWidget;
|
||||||
|
|
||||||
|
pub mod emulator_settings_ui;
|
||||||
|
pub mod serial_min_settings_ui;
|
||||||
|
|
||||||
|
|
||||||
|
pub struct DeviceSettingsUiBuilder
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl DeviceSettingsUiBuilder
|
||||||
|
{
|
||||||
|
pub fn build(device: DeviceReference) -> Box<dyn UiComponentConnectorWidget>
|
||||||
|
{
|
||||||
|
match device.as_ref()
|
||||||
|
{
|
||||||
|
Device::Emulator(_) => Box::new(EmulatorSettingsUi::builder().build(EmulatorSettingsUiInit { device })),
|
||||||
|
Device::SerialMin(_) => Box::new(SerialMinSettingsUi::builder().build(SerialMinSettingsUiInit { device }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,15 +1,13 @@
|
|||||||
use gtk::StringList;
|
|
||||||
use gtk::glib::clone;
|
use gtk::glib::clone;
|
||||||
use relm4::component::Connector;
|
use gtk::prelude::*;
|
||||||
use relm4::prelude::*;
|
use gtk::StringList;
|
||||||
use relm4::gtk::prelude::*;
|
use massiveknob_backend::orchestrator::DeviceReference;
|
||||||
use serialport::SerialPortInfo;
|
use crate::ui::uicomponent::UiComponent;
|
||||||
|
use crate::ui::uicomponent::UiComponentState;
|
||||||
use crate::ui::EmbeddedWidgetConnector;
|
|
||||||
|
|
||||||
|
|
||||||
#[tracker::track]
|
#[tracker::track]
|
||||||
pub struct SerialMinSettingsWidget
|
pub struct SerialMinSettingsUi
|
||||||
{
|
{
|
||||||
#[do_not_track]
|
#[do_not_track]
|
||||||
ports: Vec<String>,
|
ports: Vec<String>,
|
||||||
@ -19,28 +17,130 @@ pub struct SerialMinSettingsWidget
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
pub struct SerialMinSettingsUiInit
|
||||||
pub enum SerialMinSettingsWidgetMessage
|
|
||||||
{
|
{
|
||||||
PortChanged(usize),
|
pub device: DeviceReference
|
||||||
CustomPortChanged(String)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pub struct SerialMinSettingsInit
|
pub struct SerialMinSettingsUiWidgets
|
||||||
{
|
|
||||||
// this needs a good design - we need to be able to modify the device instance, but
|
|
||||||
// we can't pass the reference or an Rc to the device due to the design of create_settings_widget.
|
|
||||||
// Either seperate the UI from the Device, or pass a middle man (sender/receiver style perhaps?)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
pub struct SerialMinSettingsWidgets
|
|
||||||
{
|
{
|
||||||
custom_port_input: gtk::Entry
|
custom_port_input: gtk::Entry
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
impl UiComponent for SerialMinSettingsUi
|
||||||
|
{
|
||||||
|
type Root = gtk::Box;
|
||||||
|
type Widgets = SerialMinSettingsUiWidgets;
|
||||||
|
type Init = SerialMinSettingsUiInit;
|
||||||
|
type State = Self;
|
||||||
|
|
||||||
|
|
||||||
|
fn build_root(_init: &Self::Init) -> Self::Root
|
||||||
|
{
|
||||||
|
gtk::Box::builder()
|
||||||
|
.hexpand(true)
|
||||||
|
.orientation(gtk::Orientation::Vertical)
|
||||||
|
.spacing(8)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_widgets(root: &Self::Root, init: &Self::Init) -> Self::Widgets
|
||||||
|
{
|
||||||
|
let port_label = gtk::Label::builder()
|
||||||
|
.label(t!("serial_min.settings.port.label"))
|
||||||
|
.halign(gtk::Align::Start)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
root.append(&port_label);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
let port_model_vec: Vec<&str> = ports.iter().map(|p| p.as_str()).collect();
|
||||||
|
let port_model = StringList::new(&port_model_vec);
|
||||||
|
|
||||||
|
port_model.append(t!("serial_min.settings.port.custom").as_ref());
|
||||||
|
|
||||||
|
let port_select = gtk::DropDown::builder()
|
||||||
|
.model(&port_model)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
root.append(&port_select);
|
||||||
|
|
||||||
|
|
||||||
|
let port_select_cloned = port_select.clone();
|
||||||
|
port_select.connect_selected_notify(clone!(
|
||||||
|
move |_|
|
||||||
|
{
|
||||||
|
let active_index = port_select_cloned.selected();
|
||||||
|
if active_index == gtk::ffi::GTK_INVALID_LIST_POSITION { return };
|
||||||
|
|
||||||
|
if let Ok(active_index_usize) = usize::try_from(active_index)
|
||||||
|
{
|
||||||
|
//sender.input(SerialMinSettingsWidgetMessage::PortChanged(active_index_usize));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
let custom_port_input = gtk::Entry::builder()
|
||||||
|
.hexpand(true)
|
||||||
|
.placeholder_text(t!("serial_min.settings.custom_port_placeholder"))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
root.append(&custom_port_input);
|
||||||
|
|
||||||
|
|
||||||
|
let custom_port_input_cloned = custom_port_input.clone();
|
||||||
|
/*
|
||||||
|
custom_port_input.connect_changed(clone!(
|
||||||
|
@strong sender => move |_|
|
||||||
|
{
|
||||||
|
sender.input(SerialMinSettingsWidgetMessage::CustomPortChanged(String::from(custom_port_input_cloned.text().as_str())));
|
||||||
|
}
|
||||||
|
));
|
||||||
|
*/
|
||||||
|
|
||||||
|
Self::Widgets
|
||||||
|
{
|
||||||
|
custom_port_input
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn init(root: &Self::Root, state: &std::rc::Rc<std::cell::RefCell<Self::State>>)
|
||||||
|
{
|
||||||
|
|
||||||
|
.selected(ports.len().try_into().unwrap_or(gtk::ffi::GTK_INVALID_LIST_POSITION))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl UiComponentState<SerialMinSettingsUi> for SerialMinSettingsUi
|
||||||
|
{
|
||||||
|
fn new(_init: <SerialMinSettingsUi as UiComponent>::Init, _widgets: <SerialMinSettingsUi as UiComponent>::Widgets) -> Self
|
||||||
|
{
|
||||||
|
let ports_list = serialport::available_ports().unwrap_or_default();
|
||||||
|
let ports: Vec<String> = ports_list.iter().map(|p| p.port_name.clone()).collect();
|
||||||
|
|
||||||
|
|
||||||
|
// TODO read settings
|
||||||
|
|
||||||
|
Self
|
||||||
|
{
|
||||||
|
ports,
|
||||||
|
custom_port: String::default(),
|
||||||
|
custom_port_visible: false,
|
||||||
|
tracker: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
impl SimpleComponent for SerialMinSettingsWidget
|
impl SimpleComponent for SerialMinSettingsWidget
|
||||||
{
|
{
|
||||||
type Init = SerialMinSettingsInit;
|
type Init = SerialMinSettingsInit;
|
||||||
@ -62,81 +162,7 @@ impl SimpleComponent for SerialMinSettingsWidget
|
|||||||
|
|
||||||
fn init(_data: Self::Init, root: Self::Root, sender: ComponentSender<Self>, ) -> ComponentParts<Self>
|
fn init(_data: Self::Init, root: Self::Root, sender: ComponentSender<Self>, ) -> ComponentParts<Self>
|
||||||
{
|
{
|
||||||
let port_label = gtk::Label::builder()
|
|
||||||
.label(t!("serial_min.settings.port.label"))
|
|
||||||
.halign(gtk::Align::Start)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
root.append(&port_label);
|
|
||||||
|
|
||||||
|
|
||||||
let ports_list = serialport::available_ports().unwrap_or(Vec::<SerialPortInfo>::new());
|
|
||||||
let ports: Vec<String> = ports_list.iter().map(|p| p.port_name.clone()).collect();
|
|
||||||
|
|
||||||
let port_model_vec: Vec<&str> = ports.iter().map(|p| p.as_str()).collect();
|
|
||||||
let port_model = StringList::new(&port_model_vec);
|
|
||||||
|
|
||||||
port_model.append(t!("serial_min.settings.port.custom").as_ref());
|
|
||||||
|
|
||||||
let port_select = gtk::DropDown::builder()
|
|
||||||
.model(&port_model)
|
|
||||||
.selected(ports.len().try_into().unwrap_or(gtk::ffi::GTK_INVALID_LIST_POSITION))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
root.append(&port_select);
|
|
||||||
|
|
||||||
|
|
||||||
let port_select_cloned = port_select.clone();
|
|
||||||
port_select.connect_selected_notify(clone!(
|
|
||||||
@strong sender => move |_|
|
|
||||||
{
|
|
||||||
let active_index = port_select_cloned.selected();
|
|
||||||
if active_index == gtk::ffi::GTK_INVALID_LIST_POSITION { return };
|
|
||||||
|
|
||||||
if let Ok(active_index_usize) = usize::try_from(active_index)
|
|
||||||
{
|
|
||||||
sender.input(SerialMinSettingsWidgetMessage::PortChanged(active_index_usize));
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
|
|
||||||
let custom_port_input = gtk::Entry::builder()
|
|
||||||
.hexpand(true)
|
|
||||||
.placeholder_text(t!("serial_min.settings.custom_port_placeholder"))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
root.append(&custom_port_input);
|
|
||||||
|
|
||||||
|
|
||||||
let custom_port_input_cloned = custom_port_input.clone();
|
|
||||||
custom_port_input.connect_changed(clone!(
|
|
||||||
@strong sender => move |_|
|
|
||||||
{
|
|
||||||
sender.input(SerialMinSettingsWidgetMessage::CustomPortChanged(String::from(custom_port_input_cloned.text().as_str())));
|
|
||||||
}
|
|
||||||
));
|
|
||||||
|
|
||||||
|
|
||||||
let model = SerialMinSettingsWidget
|
|
||||||
{
|
|
||||||
ports,
|
|
||||||
|
|
||||||
custom_port: String::new(),
|
|
||||||
custom_port_visible: true,
|
|
||||||
|
|
||||||
tracker: 0
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// TODO load settings
|
|
||||||
|
|
||||||
|
|
||||||
let widgets = SerialMinSettingsWidgets
|
|
||||||
{
|
|
||||||
custom_port_input
|
|
||||||
};
|
|
||||||
|
|
||||||
ComponentParts { model, widgets }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -180,4 +206,4 @@ impl EmbeddedWidgetConnector for Connector<SerialMinSettingsWidget>
|
|||||||
{
|
{
|
||||||
self.widget().as_ref()
|
self.widget().as_ref()
|
||||||
}
|
}
|
||||||
}
|
} */
|
53
Linux/ui-gtk/src/main.rs
Normal file
53
Linux/ui-gtk/src/main.rs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
use env_logger::Env;
|
||||||
|
use gtk::glib;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use mainwindow::MainWindow;
|
||||||
|
use mainwindow::MainWindowInit;
|
||||||
|
use massiveknob_backend::orchestrator::Orchestrator;
|
||||||
|
use ui::uicomponent::UiComponent;
|
||||||
|
|
||||||
|
|
||||||
|
const APP_ID: &str = "com.github.mvrens.massiveknob";
|
||||||
|
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate rust_i18n;
|
||||||
|
|
||||||
|
i18n!("locales");
|
||||||
|
|
||||||
|
|
||||||
|
pub mod ui;
|
||||||
|
pub mod devices;
|
||||||
|
|
||||||
|
pub mod mainwindow;
|
||||||
|
|
||||||
|
fn main() -> glib::ExitCode
|
||||||
|
{
|
||||||
|
env_logger::Builder::from_env(Env::default().default_filter_or("info"))
|
||||||
|
// .format_timestamp(None)
|
||||||
|
.init();
|
||||||
|
|
||||||
|
|
||||||
|
let app = gtk::Application::builder()
|
||||||
|
.application_id(APP_ID)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
app.connect_activate(activate);
|
||||||
|
app.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn activate(app: >k::Application)
|
||||||
|
{
|
||||||
|
let orchestrator = Arc::new(Mutex::new(Orchestrator::new()));
|
||||||
|
let mainwindow = MainWindow::builder().build(MainWindowInit
|
||||||
|
{
|
||||||
|
app: app.clone(),
|
||||||
|
orchestrator: orchestrator.clone()
|
||||||
|
});
|
||||||
|
|
||||||
|
mainwindow.root.present();
|
||||||
|
}
|
257
Linux/ui-gtk/src/mainwindow.rs
Normal file
257
Linux/ui-gtk/src/mainwindow.rs
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
use std::cell::RefCell;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
use gtk::glib;
|
||||||
|
use gtk::glib::clone;
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use massiveknob_backend::orchestrator::Orchestrator;
|
||||||
|
use massiveknob_backend::util::unique_id::UniqueId;
|
||||||
|
|
||||||
|
use crate::devices::DeviceSettingsUiBuilder;
|
||||||
|
use crate::ui::uicomponent::UiComponent;
|
||||||
|
use crate::ui::uicomponent::UiComponentState;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
pub struct MainWindowInit
|
||||||
|
{
|
||||||
|
pub app: gtk::Application,
|
||||||
|
pub orchestrator: Arc<Mutex<Orchestrator>>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct MainWindow
|
||||||
|
{
|
||||||
|
orchestrator: Arc<Mutex<Orchestrator>>,
|
||||||
|
devices_sorted: Vec<SortedDevice>,
|
||||||
|
widgets: MainWindowWidgets,
|
||||||
|
|
||||||
|
device_settings_widget: Option<gtk::Widget>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct MainWindowWidgets
|
||||||
|
{
|
||||||
|
device: MainWindowDeviceWidgets
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct MainWindowDeviceWidgets
|
||||||
|
{
|
||||||
|
devices_dropdown: gtk::DropDown,
|
||||||
|
settings_container: gtk::Box
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl UiComponent for MainWindow
|
||||||
|
{
|
||||||
|
type Root = gtk::ApplicationWindow;
|
||||||
|
type Widgets = MainWindowWidgets;
|
||||||
|
type Init = MainWindowInit;
|
||||||
|
type State = Self;
|
||||||
|
|
||||||
|
|
||||||
|
fn build_root(init: &Self::Init) -> Self::Root
|
||||||
|
{
|
||||||
|
gtk::ApplicationWindow::builder()
|
||||||
|
.application(&init.app)
|
||||||
|
.title(t!("mainwindow.title"))
|
||||||
|
.default_width(500)
|
||||||
|
.default_height(500)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_widgets(root: &Self::Root, _init: &Self::Init) -> Self::Widgets
|
||||||
|
{
|
||||||
|
let tabs = gtk::Notebook::builder().build();
|
||||||
|
root.set_child(Some(&tabs));
|
||||||
|
|
||||||
|
Self::Widgets
|
||||||
|
{
|
||||||
|
device: Self::build_device_tab(&tabs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn init(_root: &Self::Root, state: &Rc<RefCell<Self::State>>)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
let state_borrowed = state.borrow();
|
||||||
|
let devices_dropdown = state_borrowed.widgets.device.devices_dropdown.clone();
|
||||||
|
let orchestrator = state_borrowed.orchestrator.lock().unwrap();
|
||||||
|
|
||||||
|
let active_device_id = orchestrator.active_device_id();
|
||||||
|
let mut active_device_index = gtk::ffi::GTK_INVALID_LIST_POSITION;
|
||||||
|
let devices_dropdown_list = gtk::StringList::default();
|
||||||
|
|
||||||
|
for (index, device) in state_borrowed.devices_sorted.iter().enumerate()
|
||||||
|
{
|
||||||
|
devices_dropdown_list.append(device.name.as_str());
|
||||||
|
|
||||||
|
if let Some(device_id) = &active_device_id
|
||||||
|
{
|
||||||
|
if *device_id == device.unique_id
|
||||||
|
{
|
||||||
|
active_device_index = index as u32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
devices_dropdown.set_model(Some(&devices_dropdown_list));
|
||||||
|
devices_dropdown.set_selected(active_device_index);
|
||||||
|
|
||||||
|
devices_dropdown.connect_selected_notify(clone!(
|
||||||
|
#[weak]
|
||||||
|
state,
|
||||||
|
|
||||||
|
move |_|
|
||||||
|
{
|
||||||
|
let mut state = state.borrow_mut();
|
||||||
|
state.update_active_device(true);
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut state = state.borrow_mut();
|
||||||
|
state.update_active_device(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl UiComponentState<MainWindow> for MainWindow
|
||||||
|
{
|
||||||
|
fn new(init: MainWindowInit, widgets: MainWindowWidgets) -> Self
|
||||||
|
{
|
||||||
|
let mut devices_sorted: Vec<SortedDevice>;
|
||||||
|
{
|
||||||
|
let orchestrator = init.orchestrator.lock().unwrap();
|
||||||
|
|
||||||
|
devices_sorted = orchestrator.devices()
|
||||||
|
.map(|device| SortedDevice
|
||||||
|
{
|
||||||
|
unique_id: device.unique_id.clone(),
|
||||||
|
name: device.name()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
devices_sorted.sort_by(|a, b| a.name.to_lowercase().cmp(&b.name.to_lowercase()));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Self
|
||||||
|
{
|
||||||
|
orchestrator: init.orchestrator.clone(),
|
||||||
|
devices_sorted,
|
||||||
|
widgets,
|
||||||
|
|
||||||
|
device_settings_widget: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl MainWindow
|
||||||
|
{
|
||||||
|
fn build_device_tab(tabs: >k::Notebook) -> MainWindowDeviceWidgets
|
||||||
|
{
|
||||||
|
//let sender = self.sender;
|
||||||
|
let tab = Self::build_box_tab(tabs, "mainwindow.tab.device");
|
||||||
|
|
||||||
|
let label = gtk::Label::builder()
|
||||||
|
.label(t!("mainwindow.deviceType.label"))
|
||||||
|
.halign(gtk::Align::Start)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
tab.append(&label);
|
||||||
|
|
||||||
|
|
||||||
|
let devices_dropdown = gtk::DropDown::builder().build();
|
||||||
|
tab.append(&devices_dropdown);
|
||||||
|
|
||||||
|
|
||||||
|
let settings_container = gtk::Box::builder().build();
|
||||||
|
tab.append(&settings_container);
|
||||||
|
|
||||||
|
|
||||||
|
MainWindowDeviceWidgets
|
||||||
|
{
|
||||||
|
devices_dropdown,
|
||||||
|
settings_container
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn build_box_tab(notebook: >k::Notebook, title_key: &str) -> gtk::Box
|
||||||
|
{
|
||||||
|
let tab = gtk::Box::builder()
|
||||||
|
.orientation(gtk::Orientation::Vertical)
|
||||||
|
.spacing(8)
|
||||||
|
.margin_start(8)
|
||||||
|
.margin_end(8)
|
||||||
|
.margin_top(8)
|
||||||
|
.margin_bottom(8)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let tab_label = gtk::Label::builder()
|
||||||
|
.label(t!(title_key))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
notebook.append_page(&tab, Some(&tab_label));
|
||||||
|
|
||||||
|
tab
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn update_active_device(&mut self, set_active: bool)
|
||||||
|
{
|
||||||
|
log::info!("update_active_device");
|
||||||
|
|
||||||
|
let active_index = self.widgets.device.devices_dropdown.selected();
|
||||||
|
if active_index == gtk::ffi::GTK_INVALID_LIST_POSITION { return };
|
||||||
|
|
||||||
|
let Ok(active_index_usize) = usize::try_from(active_index) else { return };
|
||||||
|
let selected_device = &self.devices_sorted[active_index_usize];
|
||||||
|
let device;
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut orchestrator = self.orchestrator.lock().unwrap();
|
||||||
|
|
||||||
|
if set_active
|
||||||
|
{
|
||||||
|
device = Some(orchestrator.set_active_device_id(&selected_device.unique_id));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
device = orchestrator.active_device();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(prev_widget) = &self.device_settings_widget
|
||||||
|
{
|
||||||
|
self.widgets.device.settings_container.remove(prev_widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(device) = device
|
||||||
|
{
|
||||||
|
let widget = DeviceSettingsUiBuilder::build(device.clone());
|
||||||
|
|
||||||
|
self.widgets.device.settings_container.append(&widget);
|
||||||
|
self.device_settings_widget = Some(widget);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self.device_settings_widget = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct SortedDevice
|
||||||
|
{
|
||||||
|
unique_id: UniqueId,
|
||||||
|
name: String
|
||||||
|
}
|
8
Linux/ui-gtk/src/ui/mod.rs
Normal file
8
Linux/ui-gtk/src/ui/mod.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
pub mod uicomponent;
|
||||||
|
|
||||||
|
|
||||||
|
#[deprecated]
|
||||||
|
pub trait EmbeddedWidgetConnector
|
||||||
|
{
|
||||||
|
fn root(&self) -> gtk::Widget;
|
||||||
|
}
|
104
Linux/ui-gtk/src/ui/uicomponent.rs
Normal file
104
Linux/ui-gtk/src/ui/uicomponent.rs
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
// Much credit to Relm4 for the inspiration. I initially used it, but felt
|
||||||
|
// like I wasn't using much of the abstractions it provided. But some parts
|
||||||
|
// are still very useful and I recommend giving it a try for your project!
|
||||||
|
|
||||||
|
use gtk::prelude::*;
|
||||||
|
use std::cell::RefCell;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
|
||||||
|
pub trait UiComponent : Sized
|
||||||
|
{
|
||||||
|
type Root : IsA<gtk::Widget>;
|
||||||
|
type Widgets;
|
||||||
|
type Init;
|
||||||
|
type State : UiComponentState<Self>;
|
||||||
|
|
||||||
|
|
||||||
|
fn builder() -> UiComponentBuilder<Self>
|
||||||
|
{
|
||||||
|
UiComponentBuilder::<Self>::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn build_root(init: &Self::Init) -> Self::Root;
|
||||||
|
fn build_widgets(root: &Self::Root, init: &Self::Init) -> Self::Widgets;
|
||||||
|
|
||||||
|
fn init(root: &Self::Root, state: &Rc<RefCell<Self::State>>);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub trait UiComponentState<C: UiComponent>
|
||||||
|
{
|
||||||
|
fn new(init: C::Init, widgets: C::Widgets) -> Self;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct UiComponentConnector<C: UiComponent>
|
||||||
|
{
|
||||||
|
pub root: C::Root,
|
||||||
|
pub state: Rc<RefCell<C::State>>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub trait UiComponentConnectorWidget
|
||||||
|
{
|
||||||
|
fn root(&self) -> gtk::Widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct UiComponentBuilder<C: UiComponent>
|
||||||
|
{
|
||||||
|
marker: PhantomData<C>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<C: UiComponent> Default for UiComponentBuilder<C>
|
||||||
|
{
|
||||||
|
fn default() -> Self
|
||||||
|
{
|
||||||
|
Self
|
||||||
|
{
|
||||||
|
marker: PhantomData::<C>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<C: UiComponent> UiComponentBuilder<C>
|
||||||
|
{
|
||||||
|
pub fn build(&self, init: C::Init) -> UiComponentConnector<C>
|
||||||
|
{
|
||||||
|
let root = C::build_root(&init);
|
||||||
|
let widgets = C::build_widgets(&root, &init);
|
||||||
|
let state = Rc::new(RefCell::new(C::State::new(init, widgets)));
|
||||||
|
|
||||||
|
C::init(&root, &state);
|
||||||
|
|
||||||
|
|
||||||
|
UiComponentConnector::<C>
|
||||||
|
{
|
||||||
|
root,
|
||||||
|
state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<C: UiComponent> From<UiComponentConnector<C>> for gtk::Widget
|
||||||
|
{
|
||||||
|
fn from(val: UiComponentConnector<C>) -> Self
|
||||||
|
{
|
||||||
|
val.root.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<C: UiComponent> UiComponentConnectorWidget for UiComponentConnector<C>
|
||||||
|
{
|
||||||
|
fn root(&self) -> gtk::Widget
|
||||||
|
{
|
||||||
|
self.root.clone().into()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user