feat: WIP 界面调整。

This commit is contained in:
Ivan Li 2022-12-17 19:31:22 +08:00
parent 1c38fd970e
commit 082fcaee20
13 changed files with 400 additions and 402 deletions

View File

@ -13,6 +13,7 @@
"@tauri-apps/api": "^1.1.0", "@tauri-apps/api": "^1.1.0",
"clsx": "^1.2.1", "clsx": "^1.2.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-async-hook": "^4.0.0",
"react-dom": "^18.2.0" "react-dom": "^18.2.0"
}, },
"devDependencies": { "devDependencies": {

File diff suppressed because it is too large Load Diff

View File

@ -10,20 +10,9 @@ mod rpc;
use crate::core::AmbientLightMode; use crate::core::AmbientLightMode;
use crate::core::CoreManager; use crate::core::CoreManager;
use paris::*; use paris::*;
use picker::led_color::LedColor;
use picker::manager::Picker; use picker::manager::Picker;
use std::vec; use std::vec;
#[tauri::command]
async fn refresh_displays() {
match Picker::global().refresh_displays().await {
Ok(_) => {}
Err(error) => {
error!("{}", error)
}
}
}
#[tauri::command] #[tauri::command]
async fn take_snapshot() -> Vec<String> { async fn take_snapshot() -> Vec<String> {
let manager = Picker::global(); let manager = Picker::global();
@ -45,17 +34,10 @@ async fn take_snapshot() -> Vec<String> {
} }
#[tauri::command] #[tauri::command]
async fn get_led_strip_colors() -> Result<Vec<LedColor>, String> { fn get_picker_config() -> picker::config::Configuration {
let colors = Picker::global().get_led_strip_colors().await; let configuration = picker::config::Manager::global().get_config();
match colors { info!("configuration: {:?}", configuration);
Ok(colors) => { configuration
rpc::manager::Manager::global()
.publish_led_colors(&colors.to_vec())
.await;
Ok(colors)
}
Err(error) => Err(format!("{}", error)),
}
} }
#[tauri::command] #[tauri::command]
@ -71,9 +53,8 @@ async fn main() {
tauri::Builder::default() tauri::Builder::default()
.invoke_handler(tauri::generate_handler![ .invoke_handler(tauri::generate_handler![
take_snapshot, take_snapshot,
refresh_displays,
get_led_strip_colors,
play_mode, play_mode,
get_picker_config,
]) ])
.run(tauri::generate_context!()) .run(tauri::generate_context!())
.expect("error while running tauri application"); .expect("error while running tauri application");

View File

@ -1,13 +1,13 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Serialize, Deserialize)] #[derive(Clone, Copy, Serialize, Deserialize, Debug)]
pub struct LedStripConfig { pub struct LedStripConfig {
pub index: usize, pub index: usize,
pub global_start_position: usize, pub global_start_position: usize,
pub global_end_position: usize, pub global_end_position: usize,
} }
#[derive(Clone, Copy, Serialize, Deserialize)] #[derive(Clone, Copy, Serialize, Deserialize, Debug)]
pub struct DisplayConfig { pub struct DisplayConfig {
pub index_of_display: usize, pub index_of_display: usize,
pub display_width: usize, pub display_width: usize,

View File

@ -11,10 +11,10 @@ use tauri::api::path::config_dir;
use super::DisplayConfig; use super::DisplayConfig;
#[derive(Serialize, Deserialize, Clone)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct Configuration { pub struct Configuration {
config_version: u8, pub config_version: u8,
display_configs: Vec<DisplayConfig>, pub display_configs: Vec<DisplayConfig>,
} }
impl Configuration { impl Configuration {
@ -77,6 +77,10 @@ impl Manager {
.map_err(|error| anyhow::anyhow!("can not write config file. {}", error))?; .map_err(|error| anyhow::anyhow!("can not write config file. {}", error))?;
Ok(()) Ok(())
} }
pub fn get_config(&self) -> Configuration {
self.config.clone()
}
} }
#[cfg(test)] #[cfg(test)]
@ -114,7 +118,8 @@ mod tests {
}) })
.to_string() .to_string()
.as_bytes(), .as_bytes(),
).unwrap(); )
.unwrap();
let _manager = let _manager =
crate::picker::config::manger::Manager::read_config_from_disk(config_file_path.clone()) crate::picker::config::manger::Manager::read_config_from_disk(config_file_path.clone())
.unwrap(); .unwrap();

View File

@ -2,15 +2,12 @@ use futures::{stream::FuturesUnordered, StreamExt};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use paris::info; use paris::info;
use scrap::Display; use scrap::Display;
use std::sync::Arc; use std::{sync::Arc, borrow::Borrow};
use tokio::{sync::Mutex, task}; use tokio::{sync::Mutex, task};
use crate::picker::{config::LedStripConfig, screen::Screen}; use crate::picker::{config, screen::Screen};
use super::{ use super::{config::DisplayConfig, display_picker::DisplayPicker, screenshot::Screenshot};
config::DisplayConfig, display_picker::DisplayPicker, led_color::LedColor,
screenshot::Screenshot,
};
pub struct Picker { pub struct Picker {
pub screens: Arc<Mutex<Vec<Screen>>>, pub screens: Arc<Mutex<Vec<Screen>>>,
@ -25,64 +22,14 @@ impl Picker {
SCREEN_COLOR_PICKER.get_or_init(|| Picker { SCREEN_COLOR_PICKER.get_or_init(|| Picker {
screens: Arc::new(Mutex::new(vec![])), screens: Arc::new(Mutex::new(vec![])),
screenshots: Arc::new(Mutex::new(vec![])), screenshots: Arc::new(Mutex::new(vec![])),
display_configs: Arc::new(Mutex::new(vec![ display_configs: Arc::new(Mutex::new(
DisplayConfig { config::Manager::global().get_config().display_configs,
index_of_display: 1, )),
display_width: 1920,
display_height: 1200,
top_led_strip: LedStripConfig {
index: 1,
global_start_position: 59,
global_end_position: 32,
},
bottom_led_strip: LedStripConfig {
index: 0,
global_start_position: 0,
global_end_position: 0,
},
left_led_strip: LedStripConfig {
index: 0,
global_start_position: 0,
global_end_position: 0,
},
right_led_strip: LedStripConfig {
index: 0,
global_start_position: 0,
global_end_position: 0,
},
},
DisplayConfig {
index_of_display: 0,
display_width: 3008,
display_height: 1692,
top_led_strip: LedStripConfig {
index: 0,
global_start_position: 31,
global_end_position: 0,
},
bottom_led_strip: LedStripConfig {
index: 0,
global_start_position: 0,
global_end_position: 0,
},
left_led_strip: LedStripConfig {
index: 0,
global_start_position: 0,
global_end_position: 0,
},
right_led_strip: LedStripConfig {
index: 0,
global_start_position: 0,
global_end_position: 0,
},
},
])),
}) })
} }
pub async fn list_displays(&self) -> anyhow::Result<Vec<String>> { pub async fn list_displays(&self) -> anyhow::Result<Vec<String>> {
let mut configs = self.display_configs.lock().await; let mut configs = self.display_configs.lock().await;
let screenshots = self.screenshots.lock().await;
let displays = Display::all() let displays = Display::all()
.map_err(|error| anyhow::anyhow!("Can not get all of displays. {}", error))?; .map_err(|error| anyhow::anyhow!("Can not get all of displays. {}", error))?;
@ -97,12 +44,9 @@ impl Picker {
configs.push(config); configs.push(config);
} }
for (index, display) in displays.iter().enumerate() { for config in configs.iter() {
let height = display.height();
let width = display.width();
let config = configs[index];
futs.push(async move { futs.push(async move {
let join = task::spawn(Self::preview_display_by_config(config)); let join = task::spawn(Self::preview_display_by_config(config.clone()));
join.await? join.await?
}); });
} }
@ -128,55 +72,4 @@ impl Picker {
anyhow::Ok(screenshot.to_webp_base64().await) anyhow::Ok(screenshot.to_webp_base64().await)
} }
pub async fn refresh_displays(&self) -> anyhow::Result<()> {
// let displays = Display::all()
// .map_err(|error| anyhow::anyhow!("Can not get all of displays. {}", error))?;
// let mut screens = self.screens.lock().await;
// let mut screenshots = self.screenshots.lock().await;
// screens.clear();
// info!("number of displays: {}", displays.len());
// for display in displays {
// let height = display.height();
// let width = display.width();
// match Capturer::new(display) {
// Ok(capturer) => screens.push(Screen::new(capturer, width, height)),
// Err(error) => screens.push(Screen::new_failed(
// anyhow::anyhow!("{}", error),
// width,
// height,
// )),
// };
// screenshots.push(Screenshot::new(width, height));
// }
// screens.reverse();
// screenshots.reverse();
Ok(())
}
pub async fn take_screenshots_for_all(&self) -> anyhow::Result<Vec<Screenshot>> {
let mut screens = self.screens.lock().await;
let screenshots = self.screenshots.lock().await;
for (index, screen) in screens.iter_mut().enumerate() {
let bitmap = screen.take().map_err(|error| {
anyhow::anyhow!("take screenshot for display failed. {}", error)
})?;
}
Ok(screenshots.to_vec())
}
pub async fn get_led_strip_colors(&self) -> anyhow::Result<Vec<LedColor>> {
let screenshots = self.screenshots.lock().await;
let mut colors = Vec::new();
for screenshot in screenshots.iter() {
let result = screenshot
.get_top_colors()
.await
.map_err(|error| anyhow::anyhow!("get top colors failed. {}", error))?;
colors.extend_from_slice(&result);
}
Ok(colors)
}
} }

View File

@ -1,8 +1,9 @@
import { useCallback, useState } from 'react'; import { useCallback, useEffect, useState } from 'react';
import reactLogo from './assets/react.svg'; import reactLogo from './assets/react.svg';
import { invoke } from '@tauri-apps/api/tauri'; import { invoke } from '@tauri-apps/api/tauri';
import './App.css'; import './App.css';
import clsx from 'clsx'; import clsx from 'clsx';
import { Configurator } from './configurator/configurator';
type Mode = 'Flowing' | 'Follow' | null; type Mode = 'Flowing' | 'Follow' | null;
@ -17,10 +18,6 @@ function App() {
setScreenshots(base64TextList.map((text) => `data:image/webp;base64,${text}`)); setScreenshots(base64TextList.map((text) => `data:image/webp;base64,${text}`));
} }
const refreshDisplays = useCallback(async () => {
await invoke('refresh_displays');
}, []);
const getLedStripColors = useCallback(async () => { const getLedStripColors = useCallback(async () => {
setLedStripColors(await invoke('get_led_strip_colors')); setLedStripColors(await invoke('get_led_strip_colors'));
}, []); }, []);
@ -60,7 +57,7 @@ function App() {
<button <button
className="bg-black bg-opacity-20" className="bg-black bg-opacity-20"
type="button" type="button"
onClick={() => refreshDisplays()} onClick={() => readPickerConfig()}
> >
Refresh Displays Refresh Displays
</button> </button>
@ -99,9 +96,7 @@ function App() {
</div> </div>
<div className="flex gap-5 justify-center"> <div className="flex gap-5 justify-center">
<img src="/vite.svg" className="logo vite" alt="Vite logo" /> <Configurator />
<img src="/tauri.svg" className="logo tauri" alt="Tauri logo" />
<img src={reactLogo} className="logo react" alt="React logo" />
</div> </div>
</div> </div>
); );

View File

@ -0,0 +1,25 @@
import { HTMLAttributes } from 'react';
import { FC } from 'react';
import { DisplayConfig } from '../models/display-config';
import { LedStrip } from './led-strip';
export interface DisplayWithLedStripsProps extends HTMLAttributes<HTMLElement> {
config: DisplayConfig;
screenshot: string;
}
export const DisplayWithLedStrips: FC<DisplayWithLedStripsProps> = ({
config,
screenshot,
...htmlAttrs
}) => {
return (
<section className="m-4 grid grid-rows-3 grid-cols-3 gr" {...htmlAttrs}>
<img src={screenshot} className="row-start-2 col-start-2" />
<LedStrip config={config.top_led_strip} className="row-start-1 col-start-2 h-1" />
<LedStrip config={config.left_led_strip} className="row-start-2 col-start-1 w-1" />
<LedStrip config={config.right_led_strip} className="row-start-2 col-start-3" />
<LedStrip config={config.bottom_led_strip} className="row-start-3 col-start-2" />
</section>
);
};

View File

@ -0,0 +1,11 @@
import { HTMLAttributes } from 'react';
import { FC } from 'react';
import { LedStripConfig } from '../models/led-strip-config';
export interface LedStripProps extends HTMLAttributes<HTMLElement> {
config: LedStripConfig;
}
export const LedStrip: FC<LedStripProps> = ({ config, ...htmlAttrs }) => {
return <section {...htmlAttrs}>...</section>;
};

View File

@ -0,0 +1,46 @@
import { invoke } from '@tauri-apps/api';
import { FC, useMemo } from 'react';
import { useAsync } from 'react-async-hook';
import { DisplayWithLedStrips } from './components/display-with-led-strips';
import { PickerConfiguration } from './models/picker-configuration';
const getPickerConfig = () => invoke<PickerConfiguration>('get_picker_config');
const getScreenshotOfDisplays = () =>
invoke<string[]>('take_snapshot').then((items) =>
items?.map((it) => `data:image/webp;base64,${it}`),
);
export const Configurator: FC = () => {
const { loading: pendingPickerConfig, result: pickerConfig } = useAsync(
getPickerConfig,
[],
);
const { loading: pendingScreenshotOfDisplays, result: screenshotOfDisplays } = useAsync(
getScreenshotOfDisplays,
[],
);
const displays = useMemo(() => {
if (pickerConfig && screenshotOfDisplays) {
return screenshotOfDisplays.map((screenshot, index) => (
<DisplayWithLedStrips
key={index}
config={pickerConfig.display_configs[index] ?? {}}
screenshot={screenshot}
/>
));
}
}, [pickerConfig, screenshotOfDisplays]);
if (pendingPickerConfig || pendingScreenshotOfDisplays) {
return (
<section>
{JSON.stringify({ pendingPickerConfig, pendingScreenshotOfDisplays })}
{displays}
</section>
);
}
return <section>{displays}</section>;
};

View File

@ -0,0 +1,11 @@
import { LedStripConfig } from './led-strip-config';
export class DisplayConfig {
index_of_display!: number;
display_width!: number;
display_height!: number;
top_led_strip!: LedStripConfig;
bottom_led_strip!: LedStripConfig;
left_led_strip!: LedStripConfig;
right_led_strip!: LedStripConfig;
}

View File

@ -0,0 +1,5 @@
export class LedStripConfig {
index!: number;
global_start_position!: number;
global_end_position!: number;
}

View File

@ -0,0 +1,6 @@
import { DisplayConfig } from './display-config';
export class PickerConfiguration {
config_version!: number;
display_configs!: DisplayConfig[];
}