feature/gui-configuration:支持从 GUI 配置程序。 #4

Merged
Ivan merged 19 commits from feature/gui-configuration into master 2023-01-26 23:44:19 +08:00
4 changed files with 80 additions and 31 deletions
Showing only changes of commit af03b22d05 - Show all commits

View File

@ -3,7 +3,11 @@ use once_cell::sync::OnceCell;
use paris::{error, info}; use paris::{error, info};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{collections::{HashMap, HashSet}, sync::Arc, time::Duration}; use std::{
collections::{HashMap, HashSet},
sync::Arc,
time::Duration,
};
use tauri::async_runtime::RwLock; use tauri::async_runtime::RwLock;
use tokio::{ use tokio::{
sync::mpsc, sync::mpsc,
@ -13,7 +17,7 @@ use tracing::warn;
use crate::{ use crate::{
picker::{ picker::{
config::DisplayConfig, display_picker::DisplayPicker, led_color::LedColor, manager::Picker, self, config::DisplayConfig, display_picker::DisplayPicker, led_color::LedColor,
screenshot::Screenshot, screenshot::Screenshot,
}, },
rpc, rpc,
@ -90,12 +94,16 @@ impl CoreManager {
pub async fn play_follow(&self) -> anyhow::Result<()> { pub async fn play_follow(&self) -> anyhow::Result<()> {
let mut futs = vec![]; let mut futs = vec![];
let configs = Picker::global().await.display_configs.lock().await; let configs = picker::config::Manager::global().reload_config().await;
let configs = match configs {
Ok(c) => c.display_configs,
Err(err) => anyhow::bail!("can not get display configs. {:?}", err),
};
info!("piker display configs: {:?}", configs); info!("piker display configs: {:?}", configs);
let (tx, mut rx) = mpsc::channel(10); let (tx, mut rx) = mpsc::channel(10);
for config in configs.to_owned() { for config in configs.clone() {
let tx = tx.clone(); let tx = tx.clone();
let fut = tokio::spawn(async move { let fut = tokio::spawn(async move {
match Self::follow_display_by_config(config, tx).await { match Self::follow_display_by_config(config, tx).await {
@ -108,8 +116,7 @@ impl CoreManager {
futs.push(fut); futs.push(fut);
} }
let total_colors_count = configs let total_colors_count = configs.iter()
.iter()
.flat_map(|c| { .flat_map(|c| {
vec![ vec![
c.led_strip_of_borders.top, c.led_strip_of_borders.top,
@ -125,7 +132,8 @@ impl CoreManager {
None => { None => {
vec![] vec![]
} }
}).collect::<HashSet<_>>() })
.collect::<HashSet<_>>()
.len(); .len();
tokio::spawn(async move { tokio::spawn(async move {
let mut global_colors = HashMap::new(); let mut global_colors = HashMap::new();

View File

@ -2,7 +2,8 @@ use std::{
env::current_dir, env::current_dir,
fs::{self, File}, fs::{self, File},
io::Read, io::Read,
path::PathBuf, sync::Arc, path::PathBuf,
sync::Arc,
}; };
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
@ -43,7 +44,9 @@ impl Manager {
} }
pub fn new(config: Configuration) -> Self { pub fn new(config: Configuration) -> Self {
Self { config: Arc::new(Mutex::new(config)) } Self {
config: Arc::new(Mutex::new(config)),
}
} }
pub fn get_config_file_path() -> PathBuf { pub fn get_config_file_path() -> PathBuf {
@ -70,7 +73,10 @@ impl Manager {
.map_err(|error| anyhow::anyhow!("can not parse config file contents. {}", error)) .map_err(|error| anyhow::anyhow!("can not parse config file contents. {}", error))
} }
pub fn write_config_to_disk(config_file_path: PathBuf, config: &Configuration) -> anyhow::Result<()> { pub fn write_config_to_disk(
config_file_path: PathBuf,
config: &Configuration,
) -> anyhow::Result<()> {
let contents = serde_json::to_string(config) let contents = serde_json::to_string(config)
.map_err(|error| anyhow::anyhow!("can not serialize config. {}", error))?; .map_err(|error| anyhow::anyhow!("can not serialize config. {}", error))?;
info!("contents: {}", contents); info!("contents: {}", contents);
@ -86,6 +92,13 @@ impl Manager {
let mut config = self.config.lock().await; let mut config = self.config.lock().await;
*config = new_config.clone(); *config = new_config.clone();
} }
pub async fn reload_config(&self) -> anyhow::Result<Configuration> {
let mut config = self.config.lock().await;
let new_config = Self::read_config_from_disk(Self::get_config_file_path())
.map_err(|err| anyhow::anyhow!("can not reload config. {:?}", err))?;
*config = new_config.clone();
return anyhow::Ok(new_config);
}
} }
#[cfg(test)] #[cfg(test)]
@ -103,9 +116,11 @@ mod tests {
let temp = TestDir::temp().create("config_dir", test_dir::FileType::Dir); let temp = TestDir::temp().create("config_dir", test_dir::FileType::Dir);
let config_file_path = temp.path("config_dir").join("picker.config.json"); let config_file_path = temp.path("config_dir").join("picker.config.json");
let manager = crate::picker::config::manger::Manager::default(); let manager = crate::picker::config::manger::Manager::default();
crate::picker::config::manger::Manager crate::picker::config::manger::Manager::write_config_to_disk(
::write_config_to_disk(config_file_path.clone(), &Configuration::default()) config_file_path.clone(),
.unwrap(); &Configuration::default(),
)
.unwrap();
let contents = fs::read_to_string(config_file_path.clone()).unwrap(); let contents = fs::read_to_string(config_file_path.clone()).unwrap();
let _config: Configuration = serde_json::from_str(contents.as_str()).unwrap(); let _config: Configuration = serde_json::from_str(contents.as_str()).unwrap();

View File

@ -43,21 +43,21 @@ export const DraggableStrip: FC<DraggableStripProp> = ({
const handleMouseMoveRef = useRef<(ev: MouseEvent) => void>(); const handleMouseMoveRef = useRef<(ev: MouseEvent) => void>();
const [boxTranslateX, setBoxTranslateX] = useState(0); const [boxTranslateX, setBoxTranslateX] = useState(0);
const ledItems = useMemo( const ledItems = useMemo(() => {
() => const step = config.global_start_position - config.global_end_position < 0 ? 1 : -1;
pixels.map((rgb, i) => ( return pixels.map((rgb, i) => (
<StyledPixel <StyledPixel
key={i} key={i}
rgb={rgb} rgb={rgb}
css={css` css={css`
grid-column: ${(availableConfig.global_start_position ?? 0) + i + 1} / span 1; grid-column: ${(availableConfig.global_start_position ?? 0) + i * step + 1} /
grid-row-start: ${index + 1}; span 1;
pointer-events: none; grid-row-start: ${index + 1};
`} pointer-events: none;
/> `}
)), />
[pixels, availableConfig], ));
); }, [pixels, availableConfig]);
const [placeholders, placeholderRefs]: [ReactNode[], RefObject<HTMLSpanElement>[]] = const [placeholders, placeholderRefs]: [ReactNode[], RefObject<HTMLSpanElement>[]] =
useMemo( useMemo(

View File

@ -28,7 +28,14 @@ export const LedStripEditor: FC<LedStripEditorProps> = ({
}) => { }) => {
const addLed = useCallback(() => { const addLed = useCallback(() => {
if (config) { if (config) {
onChange?.({ ...config, global_end_position: config.global_end_position + 1 }); if (config.global_start_position <= config.global_end_position) {
onChange?.({ ...config, global_end_position: config.global_end_position + 1 });
} else {
onChange?.({
...config,
global_start_position: config.global_start_position + 1,
});
}
} else { } else {
onChange?.(new LedStripConfig(0, 0, 1)); onChange?.(new LedStripConfig(0, 0, 1));
} }
@ -36,10 +43,29 @@ export const LedStripEditor: FC<LedStripEditorProps> = ({
const removeLed = useCallback(() => { const removeLed = useCallback(() => {
if (!config) { if (!config) {
onChange?.(null); onChange?.(null);
} else if (Math.abs(config.global_start_position - config.global_end_position) <= 1) {
onChange?.(null);
} else { } else {
onChange?.({ ...config, global_end_position: config.global_end_position - 1 }); if (config.global_start_position <= config.global_end_position) {
onChange?.({ ...config, global_end_position: config.global_end_position - 1 });
} else {
onChange?.({
...config,
global_start_position: config.global_start_position - 1,
});
}
} }
}, [config, onChange]); }, [config, onChange]);
const reverse = useCallback(() => {
if (!config) {
return;
}
onChange?.({
...config,
global_start_position: config.global_end_position,
global_end_position: config.global_start_position,
});
}, [config, onChange]);
return ( return (
<StyledContainer {...htmlAttrs}> <StyledContainer {...htmlAttrs}>
@ -49,7 +75,7 @@ export const LedStripEditor: FC<LedStripEditorProps> = ({
<StyledButton title="Remove LED" onClick={removeLed}> <StyledButton title="Remove LED" onClick={removeLed}>
<FontAwesomeIcon icon={faMinus} /> <FontAwesomeIcon icon={faMinus} />
</StyledButton> </StyledButton>
<StyledButton title="Reverse"> <StyledButton title="Reverse" onClick={reverse}>
<FontAwesomeIcon icon={faLeftRight} /> <FontAwesomeIcon icon={faLeftRight} />
</StyledButton> </StyledButton>
{`s: ${config?.global_start_position ?? 'x'}, e: ${ {`s: ${config?.global_start_position ?? 'x'}, e: ${