feature/gui-configuration:支持从 GUI 配置程序。 #4
@ -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();
|
||||||
|
@ -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();
|
||||||
|
@ -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(
|
||||||
|
@ -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: ${
|
||||||
|
Loading…
Reference in New Issue
Block a user