Compare commits
2 Commits
6802dbb7c0
...
458cc85db2
Author | SHA1 | Date | |
---|---|---|---|
458cc85db2 | |||
cb5fb901f9 |
@ -14,10 +14,16 @@ use paris::*;
|
|||||||
use picker::config::DisplayConfig;
|
use picker::config::DisplayConfig;
|
||||||
use picker::manager::Picker;
|
use picker::manager::Picker;
|
||||||
use picker::screenshot::ScreenshotDto;
|
use picker::screenshot::ScreenshotDto;
|
||||||
use std::vec;
|
use tauri::async_runtime::Mutex;
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
|
|
||||||
|
static GET_SCREENSHOT_LOCK: OnceCell<Mutex<bool>> = OnceCell::new();
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn take_snapshot() -> Vec<ScreenshotDto> {
|
async fn take_snapshot() -> Vec<ScreenshotDto> {
|
||||||
|
info!("Hi?");
|
||||||
|
let _lock = GET_SCREENSHOT_LOCK.get_or_init(|| Mutex::new(false)).lock().await;
|
||||||
|
info!("Hi!");
|
||||||
let manager = Picker::global().await;
|
let manager = Picker::global().await;
|
||||||
|
|
||||||
let start = time::Instant::now();
|
let start = time::Instant::now();
|
||||||
@ -38,17 +44,17 @@ async fn take_snapshot() -> Vec<ScreenshotDto> {
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn get_screenshot_by_config(config: DisplayConfig) -> Result<ScreenshotDto, String> {
|
async fn get_screenshot_by_config(config: DisplayConfig) -> Result<ScreenshotDto, String> {
|
||||||
info!("Hi");
|
info!("Hi?");
|
||||||
let manager = Picker::global();
|
// let _lock = GET_SCREENSHOT_LOCK.get_or_init(|| Mutex::new(false)).lock().await;
|
||||||
|
// info!("Hi!");
|
||||||
let start = time::Instant::now();
|
let start = time::Instant::now();
|
||||||
let screenshot_dto = manager.await.get_screenshot_by_config(config).await;
|
let screenshot_dto = Picker::preview_display_by_config(config).await;
|
||||||
info!("截图耗时 {} s", start.elapsed().as_seconds_f32());
|
info!("截图耗时 {} s", start.elapsed().as_seconds_f32());
|
||||||
match screenshot_dto {
|
match screenshot_dto {
|
||||||
Ok(screenshot_dto) => Ok(screenshot_dto),
|
Ok(screenshot_dto) => Ok(screenshot_dto),
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
error!("get_screenshot_by_config failed. {}", error);
|
error!("preview_display_by_config failed. {}", error);
|
||||||
Err(format!("get_screenshot_by_config failed. {}", error))
|
Err(format!("preview_display_by_config failed. {}", error))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,16 +84,4 @@ impl Picker {
|
|||||||
|
|
||||||
anyhow::Ok(screenshot.to_dto().await)
|
anyhow::Ok(screenshot.to_dto().await)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_screenshot_by_config(
|
|
||||||
&self,
|
|
||||||
config: DisplayConfig,
|
|
||||||
) -> anyhow::Result<ScreenshotDto> {
|
|
||||||
let start = time::Instant::now();
|
|
||||||
let mut picker = DisplayPicker::from_config(config)?;
|
|
||||||
let screenshot = picker.take_screenshot()?;
|
|
||||||
info!("Take Screenshot Spend: {}", start.elapsed());
|
|
||||||
|
|
||||||
anyhow::Ok(screenshot.to_dto().await)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
use image::ImageBuffer;
|
use image::ImageBuffer;
|
||||||
use image::{ImageOutputFormat, Rgb};
|
use image::{ImageOutputFormat, Rgb};
|
||||||
use paris::error;
|
use paris::{error, info};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
use super::{config::DisplayConfig, led_color::LedColor};
|
use super::{config::DisplayConfig, led_color::LedColor};
|
||||||
|
|
||||||
@ -265,9 +266,14 @@ impl Screenshot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub async fn to_dto(&self) -> ScreenshotDto {
|
pub async fn to_dto(&self) -> ScreenshotDto {
|
||||||
|
let rk = SystemTime::now();
|
||||||
|
info!("[{:?} {:p}] to_dto", rk.elapsed(), &self);
|
||||||
let encode_image = self.to_webp_base64().await;
|
let encode_image = self.to_webp_base64().await;
|
||||||
|
info!("[{:?} {:p}] image", rk.elapsed(), &self);
|
||||||
let config = self.config.clone();
|
let config = self.config.clone();
|
||||||
|
info!("[{:?} {:p}] cloned", rk.elapsed(), &self);
|
||||||
let colors = self.get_colors();
|
let colors = self.get_colors();
|
||||||
|
info!("[{:?} {:p}] colors", rk.elapsed(), &self);
|
||||||
ScreenshotDto {
|
ScreenshotDto {
|
||||||
encode_image,
|
encode_image,
|
||||||
config,
|
config,
|
||||||
|
@ -1,19 +1,6 @@
|
|||||||
import debug from 'debug';
|
import debug from 'debug';
|
||||||
import { isNil, lensPath, set, splitEvery, update } from 'ramda';
|
import { isNil, lensPath, set, splitEvery, update } from 'ramda';
|
||||||
import {
|
import { FC, useEffect, useMemo, useState } from 'react';
|
||||||
createRef,
|
|
||||||
FC,
|
|
||||||
Fragment,
|
|
||||||
MouseEventHandler,
|
|
||||||
ReactEventHandler,
|
|
||||||
ReactNode,
|
|
||||||
RefObject,
|
|
||||||
useCallback,
|
|
||||||
useEffect,
|
|
||||||
useMemo,
|
|
||||||
useRef,
|
|
||||||
useState,
|
|
||||||
} from 'react';
|
|
||||||
import tw, { css, styled } from 'twin.macro';
|
import tw, { css, styled } from 'twin.macro';
|
||||||
import { Borders, borders } from '../../constants/border';
|
import { Borders, borders } from '../../constants/border';
|
||||||
import { useLedCount } from '../contents/led-count';
|
import { useLedCount } from '../contents/led-count';
|
||||||
@ -21,9 +8,10 @@ import { DisplayConfig, LedStripConfigOfBorders } from '../models/display-config
|
|||||||
import { LedStripConfig } from '../models/led-strip-config';
|
import { LedStripConfig } from '../models/led-strip-config';
|
||||||
import { PixelRgb } from '../models/pixel-rgb';
|
import { PixelRgb } from '../models/pixel-rgb';
|
||||||
import { ScreenshotDto } from '../models/screenshot.dto';
|
import { ScreenshotDto } from '../models/screenshot.dto';
|
||||||
|
import { DraggableStrip } from './draggable-strip';
|
||||||
import { StyledPixel } from './styled-pixel';
|
import { StyledPixel } from './styled-pixel';
|
||||||
|
|
||||||
const logger = debug('app:completed-led-strip');
|
export const logger = debug('app:completed-led-strip');
|
||||||
|
|
||||||
interface CompletedLedStripProps {
|
interface CompletedLedStripProps {
|
||||||
screenshots: ScreenshotDto[];
|
screenshots: ScreenshotDto[];
|
||||||
@ -99,7 +87,7 @@ export const CompletedLedStrip: FC<CompletedLedStripProps> = ({
|
|||||||
}, [ledCount, borderLedStrips, overrideBorderLedStrips]);
|
}, [ledCount, borderLedStrips, overrideBorderLedStrips]);
|
||||||
|
|
||||||
const strips = useMemo(() => {
|
const strips = useMemo(() => {
|
||||||
return (overrideBorderLedStrips ?? borderLedStrips).map(({ config, pixels }, index) =>
|
return borderLedStrips.map(({ config, pixels }, index) =>
|
||||||
config ? (
|
config ? (
|
||||||
<DraggableStrip
|
<DraggableStrip
|
||||||
key={index}
|
key={index}
|
||||||
@ -110,7 +98,7 @@ export const CompletedLedStrip: FC<CompletedLedStripProps> = ({
|
|||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
onConfigFinish={(c) => {
|
onConfigFinish={(c) => {
|
||||||
const indexOfDisplay = Math.round(index / borders.length);
|
const indexOfDisplay = Math.floor(index / borders.length);
|
||||||
const xLens = lensPath<LedStripConfigOfBorders, Borders>([
|
const xLens = lensPath<LedStripConfigOfBorders, Borders>([
|
||||||
borders[index % borders.length],
|
borders[index % borders.length],
|
||||||
]);
|
]);
|
||||||
@ -122,7 +110,6 @@ export const CompletedLedStrip: FC<CompletedLedStripProps> = ({
|
|||||||
screenshots[indexOfDisplay].config.led_strip_of_borders,
|
screenshots[indexOfDisplay].config.led_strip_of_borders,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
logger('Change DisplayConfig. %o', displayConfig);
|
|
||||||
onDisplayConfigChange?.(displayConfig);
|
onDisplayConfigChange?.(displayConfig);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
@ -130,7 +117,7 @@ export const CompletedLedStrip: FC<CompletedLedStripProps> = ({
|
|||||||
<div key={index} />
|
<div key={index} />
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}, [borderLedStrips, overrideBorderLedStrips]);
|
}, [borderLedStrips, screenshots]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setOverrideBorderLedStrips(undefined);
|
setOverrideBorderLedStrips(undefined);
|
||||||
@ -143,172 +130,4 @@ export const CompletedLedStrip: FC<CompletedLedStripProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
interface DraggableStripProp {
|
|
||||||
config: LedStripConfig;
|
|
||||||
pixels: PixelRgb[];
|
|
||||||
index: number;
|
|
||||||
onConfigChange?: (config: LedStripConfig) => void;
|
|
||||||
onConfigFinish?: (config: LedStripConfig) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const DraggableStrip: FC<DraggableStripProp> = ({
|
|
||||||
config,
|
|
||||||
pixels,
|
|
||||||
index,
|
|
||||||
onConfigChange,
|
|
||||||
onConfigFinish,
|
|
||||||
}) => {
|
|
||||||
const ledItems = pixels.map((rgb, i) => (
|
|
||||||
<StyledPixel
|
|
||||||
key={i}
|
|
||||||
rgb={rgb}
|
|
||||||
css={css`
|
|
||||||
grid-column: ${(config?.global_start_position ?? 0) + i + 1} / span 1;
|
|
||||||
grid-row-start: ${index + 1};
|
|
||||||
pointer-events: none;
|
|
||||||
`}
|
|
||||||
/>
|
|
||||||
));
|
|
||||||
|
|
||||||
const { ledCount } = useLedCount();
|
|
||||||
|
|
||||||
const startXRef = useRef(0);
|
|
||||||
const currentXRef = useRef(0);
|
|
||||||
const configRef = useRef<LedStripConfig>();
|
|
||||||
// const currentDiffRef = useRef(0);
|
|
||||||
const [currentDiff, setCurrentDiff] = useState(0);
|
|
||||||
const isDragRef = useRef(false);
|
|
||||||
const handleMouseMoveRef = useRef<(ev: MouseEvent) => void>();
|
|
||||||
const [boxTranslateX, setBoxTranslateX] = useState(0);
|
|
||||||
|
|
||||||
const [placeholders, placeholderRefs]: [ReactNode[], RefObject<HTMLSpanElement>[]] =
|
|
||||||
useMemo(
|
|
||||||
() =>
|
|
||||||
new Array(ledCount)
|
|
||||||
.fill(undefined)
|
|
||||||
.map((_, i) => {
|
|
||||||
const ref = createRef<HTMLSpanElement>();
|
|
||||||
const n = (
|
|
||||||
<span
|
|
||||||
ref={ref}
|
|
||||||
key={i}
|
|
||||||
tw="opacity-30 bg-red-500 h-full w-full border border-yellow-400"
|
|
||||||
css={css`
|
|
||||||
grid-column-start: ${i + 1};
|
|
||||||
grid-row-start: ${index + 1};
|
|
||||||
`}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
return [n, ref] as [ReactNode, RefObject<HTMLSpanElement>];
|
|
||||||
})
|
|
||||||
.reduce(
|
|
||||||
([nList, refList], [n, ref]) => [
|
|
||||||
[...nList, n],
|
|
||||||
[...refList, ref],
|
|
||||||
],
|
|
||||||
[[], []] as [ReactNode[], RefObject<HTMLSpanElement>[]],
|
|
||||||
),
|
|
||||||
[ledCount],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleMouseDown: MouseEventHandler<HTMLDivElement> = useCallback(
|
|
||||||
(ev) => {
|
|
||||||
startXRef.current = ev.pageX;
|
|
||||||
ev.currentTarget.requestPointerLock();
|
|
||||||
isDragRef.current = true;
|
|
||||||
|
|
||||||
const placeholderPositions = placeholderRefs.map((it) => {
|
|
||||||
if (!it.current) {
|
|
||||||
return [0, 0];
|
|
||||||
}
|
|
||||||
const viewportOffset = it.current.getBoundingClientRect();
|
|
||||||
return [viewportOffset.left, viewportOffset.right] as [number, number];
|
|
||||||
});
|
|
||||||
|
|
||||||
logger('placeholderPositions: %o', placeholderPositions);
|
|
||||||
|
|
||||||
// set init position
|
|
||||||
const initPos = placeholderPositions.findIndex(
|
|
||||||
([l, r]) => l <= ev.pageX && r >= ev.pageX,
|
|
||||||
);
|
|
||||||
setCurrentDiff(initPos);
|
|
||||||
let prevMatch = 0;
|
|
||||||
|
|
||||||
if (handleMouseMoveRef.current) {
|
|
||||||
document.body.removeEventListener('mousemove', handleMouseMoveRef.current);
|
|
||||||
}
|
|
||||||
handleMouseMoveRef.current = (ev) => {
|
|
||||||
if (!isDragRef.current) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
currentXRef.current = ev.pageX;
|
|
||||||
setBoxTranslateX(currentXRef.current - startXRef.current);
|
|
||||||
const match = placeholderPositions.findIndex(
|
|
||||||
([l, r]) => l <= currentXRef.current && r >= currentXRef.current,
|
|
||||||
);
|
|
||||||
if (match === -1) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match === prevMatch) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
prevMatch = match;
|
|
||||||
|
|
||||||
const diff = match - initPos;
|
|
||||||
const newValue: LedStripConfig = {
|
|
||||||
...config,
|
|
||||||
global_start_position: config.global_start_position + diff,
|
|
||||||
global_end_position: config.global_end_position + diff,
|
|
||||||
};
|
|
||||||
configRef.current = newValue;
|
|
||||||
logger('change config. new: $o', newValue);
|
|
||||||
onConfigChange?.(newValue);
|
|
||||||
};
|
|
||||||
document.body.addEventListener('mousemove', handleMouseMoveRef.current);
|
|
||||||
},
|
|
||||||
[placeholderRefs],
|
|
||||||
);
|
|
||||||
|
|
||||||
// move event.
|
|
||||||
useEffect(() => {
|
|
||||||
const handleMouseUp = (ev: MouseEvent) => {
|
|
||||||
startXRef.current = 0;
|
|
||||||
isDragRef.current = false;
|
|
||||||
if (configRef.current) {
|
|
||||||
onConfigFinish?.(configRef.current);
|
|
||||||
}
|
|
||||||
document.exitPointerLock();
|
|
||||||
if (handleMouseMoveRef.current) {
|
|
||||||
document.body.removeEventListener('mousemove', handleMouseMoveRef.current);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
document.body.addEventListener('mouseup', handleMouseUp);
|
|
||||||
return () => {
|
|
||||||
document.body.removeEventListener('mouseup', handleMouseUp);
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
// reset translateX when config updated.
|
|
||||||
useEffect(() => {
|
|
||||||
startXRef.current = currentXRef.current;
|
|
||||||
setCurrentDiff(0);
|
|
||||||
}, [config]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Fragment>
|
|
||||||
{placeholders}
|
|
||||||
{ledItems}
|
|
||||||
<div
|
|
||||||
tw="border border-gray-700 h-3 w-full rounded-full"
|
|
||||||
css={css`
|
|
||||||
grid-column-start: ${(config?.global_start_position ?? 0) + 1 - currentDiff};
|
|
||||||
grid-column-end: ${(config?.global_end_position ?? 0) + 1 - currentDiff};
|
|
||||||
grid-row-start: ${index + 1};
|
|
||||||
cursor: ew-resize;
|
|
||||||
transform: translateX(${boxTranslateX}px);
|
|
||||||
`}
|
|
||||||
onMouseDown={handleMouseDown}
|
|
||||||
></div>
|
|
||||||
</Fragment>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
196
src/configurator/components/draggable-strip.tsx
Normal file
196
src/configurator/components/draggable-strip.tsx
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
import {
|
||||||
|
createRef,
|
||||||
|
FC,
|
||||||
|
Fragment,
|
||||||
|
MouseEventHandler,
|
||||||
|
ReactNode,
|
||||||
|
RefObject,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useMemo,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
import { css } from 'twin.macro';
|
||||||
|
import { useLedCount } from '../contents/led-count';
|
||||||
|
import { LedStripConfig } from '../models/led-strip-config';
|
||||||
|
import { PixelRgb } from '../models/pixel-rgb';
|
||||||
|
import { StyledPixel } from './styled-pixel';
|
||||||
|
import { logger } from './completed-led-strip';
|
||||||
|
|
||||||
|
interface DraggableStripProp {
|
||||||
|
config: LedStripConfig;
|
||||||
|
pixels: PixelRgb[];
|
||||||
|
index: number;
|
||||||
|
onConfigChange?: (config: LedStripConfig) => void;
|
||||||
|
onConfigFinish?: (config: LedStripConfig) => void;
|
||||||
|
}
|
||||||
|
export const DraggableStrip: FC<DraggableStripProp> = ({
|
||||||
|
config,
|
||||||
|
pixels,
|
||||||
|
index,
|
||||||
|
onConfigChange,
|
||||||
|
onConfigFinish,
|
||||||
|
}) => {
|
||||||
|
const { ledCount } = useLedCount();
|
||||||
|
|
||||||
|
const startXRef = useRef(0);
|
||||||
|
const currentXRef = useRef(0);
|
||||||
|
const configRef = useRef<LedStripConfig>();
|
||||||
|
const [availableConfig, setAvailableConfig] = useState<LedStripConfig>(config);
|
||||||
|
// const currentDiffRef = useRef(0);
|
||||||
|
const isDragRef = useRef(false);
|
||||||
|
const handleMouseMoveRef = useRef<(ev: MouseEvent) => void>();
|
||||||
|
const [boxTranslateX, setBoxTranslateX] = useState(0);
|
||||||
|
|
||||||
|
const ledItems = useMemo(
|
||||||
|
() =>
|
||||||
|
pixels.map((rgb, i) => (
|
||||||
|
<StyledPixel
|
||||||
|
key={i}
|
||||||
|
rgb={rgb}
|
||||||
|
css={css`
|
||||||
|
grid-column: ${(availableConfig.global_start_position ?? 0) + i + 1} / span 1;
|
||||||
|
grid-row-start: ${index + 1};
|
||||||
|
pointer-events: none;
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
)),
|
||||||
|
[pixels, availableConfig],
|
||||||
|
);
|
||||||
|
|
||||||
|
const [placeholders, placeholderRefs]: [ReactNode[], RefObject<HTMLSpanElement>[]] =
|
||||||
|
useMemo(
|
||||||
|
() =>
|
||||||
|
new Array(ledCount)
|
||||||
|
.fill(undefined)
|
||||||
|
.map((_, i) => {
|
||||||
|
const ref = createRef<HTMLSpanElement>();
|
||||||
|
const n = (
|
||||||
|
<span
|
||||||
|
ref={ref}
|
||||||
|
key={i}
|
||||||
|
tw="opacity-30 bg-red-500 h-full w-full border border-yellow-400"
|
||||||
|
css={css`
|
||||||
|
grid-column-start: ${i + 1};
|
||||||
|
grid-row-start: ${index + 1};
|
||||||
|
`}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
return [n, ref] as [ReactNode, RefObject<HTMLSpanElement>];
|
||||||
|
})
|
||||||
|
.reduce(
|
||||||
|
([nList, refList], [n, ref]) => [
|
||||||
|
[...nList, n],
|
||||||
|
[...refList, ref],
|
||||||
|
],
|
||||||
|
[[], []] as [ReactNode[], RefObject<HTMLSpanElement>[]],
|
||||||
|
),
|
||||||
|
[ledCount],
|
||||||
|
);
|
||||||
|
|
||||||
|
// start and moving
|
||||||
|
const handleMouseDown: MouseEventHandler<HTMLDivElement> = useCallback(
|
||||||
|
(ev) => {
|
||||||
|
startXRef.current = ev.pageX;
|
||||||
|
ev.currentTarget.requestPointerLock();
|
||||||
|
isDragRef.current = true;
|
||||||
|
logger('handleMouseDown, config: %o', config);
|
||||||
|
|
||||||
|
const placeholderPositions = placeholderRefs.map((it) => {
|
||||||
|
if (!it.current) {
|
||||||
|
return [0, 0];
|
||||||
|
}
|
||||||
|
const viewportOffset = it.current.getBoundingClientRect();
|
||||||
|
return [viewportOffset.left, viewportOffset.right] as [number, number];
|
||||||
|
});
|
||||||
|
|
||||||
|
logger('placeholderPositions: %o', placeholderPositions);
|
||||||
|
|
||||||
|
// set init position
|
||||||
|
const initPos = placeholderPositions.findIndex(
|
||||||
|
([l, r]) => l <= ev.pageX && r >= ev.pageX,
|
||||||
|
);
|
||||||
|
let prevMatch = 0;
|
||||||
|
|
||||||
|
if (handleMouseMoveRef.current) {
|
||||||
|
document.body.removeEventListener('mousemove', handleMouseMoveRef.current);
|
||||||
|
}
|
||||||
|
handleMouseMoveRef.current = (ev) => {
|
||||||
|
if (!isDragRef.current) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
currentXRef.current = ev.pageX;
|
||||||
|
setBoxTranslateX(currentXRef.current - startXRef.current);
|
||||||
|
const match = placeholderPositions.findIndex(
|
||||||
|
([l, r]) => l <= currentXRef.current && r >= currentXRef.current,
|
||||||
|
);
|
||||||
|
if (match === -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (match === prevMatch) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
prevMatch = match;
|
||||||
|
|
||||||
|
const diff = match - initPos;
|
||||||
|
const newValue: LedStripConfig = {
|
||||||
|
...config,
|
||||||
|
global_start_position: config.global_start_position + diff,
|
||||||
|
global_end_position: config.global_end_position + diff,
|
||||||
|
};
|
||||||
|
configRef.current = newValue;
|
||||||
|
setAvailableConfig(newValue);
|
||||||
|
logger('change config. old: %o, new: %o', config, newValue);
|
||||||
|
onConfigChange?.(newValue);
|
||||||
|
};
|
||||||
|
document.body.addEventListener('mousemove', handleMouseMoveRef.current);
|
||||||
|
},
|
||||||
|
[placeholderRefs, availableConfig, setAvailableConfig, config],
|
||||||
|
);
|
||||||
|
|
||||||
|
// move event.
|
||||||
|
useEffect(() => {
|
||||||
|
const handleMouseUp = (ev: MouseEvent) => {
|
||||||
|
if (configRef.current && isDragRef.current) {
|
||||||
|
onConfigFinish?.(configRef.current);
|
||||||
|
}
|
||||||
|
startXRef.current = 0;
|
||||||
|
isDragRef.current = false;
|
||||||
|
document.exitPointerLock();
|
||||||
|
if (handleMouseMoveRef.current) {
|
||||||
|
document.body.removeEventListener('mousemove', handleMouseMoveRef.current);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
document.body.addEventListener('mouseup', handleMouseUp);
|
||||||
|
return () => {
|
||||||
|
document.body.removeEventListener('mouseup', handleMouseUp);
|
||||||
|
};
|
||||||
|
}, [onConfigFinish]);
|
||||||
|
// reset translateX when config updated.
|
||||||
|
useEffect(() => {
|
||||||
|
startXRef.current = currentXRef.current;
|
||||||
|
setAvailableConfig(config);
|
||||||
|
setBoxTranslateX(0);
|
||||||
|
logger('useEffect, config: %o', config);
|
||||||
|
}, [config]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Fragment>
|
||||||
|
{placeholders}
|
||||||
|
{ledItems}
|
||||||
|
<div
|
||||||
|
tw="border border-gray-700 h-3 w-full rounded-full"
|
||||||
|
css={css`
|
||||||
|
grid-column-start: ${(config?.global_start_position ?? 0) + 1};
|
||||||
|
grid-column-end: ${(config?.global_end_position ?? 0) + 1};
|
||||||
|
grid-row-start: ${index + 1};
|
||||||
|
cursor: ew-resize;
|
||||||
|
transform: translateX(${boxTranslateX}px);
|
||||||
|
`}
|
||||||
|
onMouseDown={handleMouseDown}
|
||||||
|
></div>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
};
|
@ -1,7 +1,7 @@
|
|||||||
import { HTMLAttributes, useCallback } from 'react';
|
import { HTMLAttributes, useCallback } from 'react';
|
||||||
import { FC } from 'react';
|
import { FC } from 'react';
|
||||||
import { LedStripConfig } from '../models/led-strip-config';
|
import { LedStripConfig } from '../models/led-strip-config';
|
||||||
import tw, { css, styled, theme } from 'twin.macro';
|
import tw, { styled } from 'twin.macro';
|
||||||
import { faLeftRight, faMinus, faPlus } from '@fortawesome/free-solid-svg-icons';
|
import { faLeftRight, faMinus, faPlus } from '@fortawesome/free-solid-svg-icons';
|
||||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||||
|
|
||||||
@ -52,6 +52,9 @@ export const LedStripEditor: FC<LedStripEditorProps> = ({
|
|||||||
<StyledButton title="Reverse">
|
<StyledButton title="Reverse">
|
||||||
<FontAwesomeIcon icon={faLeftRight} />
|
<FontAwesomeIcon icon={faLeftRight} />
|
||||||
</StyledButton>
|
</StyledButton>
|
||||||
|
{`s: ${config?.global_start_position ?? 'x'}, e: ${
|
||||||
|
config?.global_end_position ?? 'x'
|
||||||
|
}`}
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { invoke } from '@tauri-apps/api';
|
import { invoke } from '@tauri-apps/api';
|
||||||
import { FC, Fragment, useCallback, useEffect, useMemo, useState } from 'react';
|
import { FC, useEffect, useMemo, useState } from 'react';
|
||||||
import tw, { styled } from 'twin.macro';
|
import tw, { styled } from 'twin.macro';
|
||||||
import { useAsync, useAsyncCallback } from 'react-async-hook';
|
import { useAsync, useAsyncCallback } from 'react-async-hook';
|
||||||
import { DisplayWithLedStrips } from './components/display-with-led-strips';
|
import { DisplayWithLedStrips } from './components/display-with-led-strips';
|
||||||
@ -12,6 +12,9 @@ import { faSpinner } from '@fortawesome/free-solid-svg-icons';
|
|||||||
import { update } from 'ramda';
|
import { update } from 'ramda';
|
||||||
import { CompletedLedStrip } from './components/completed-led-strip';
|
import { CompletedLedStrip } from './components/completed-led-strip';
|
||||||
import { LedCountProvider } from './contents/led-count';
|
import { LedCountProvider } from './contents/led-count';
|
||||||
|
import debug from 'debug';
|
||||||
|
|
||||||
|
const logger = debug('app:configurator');
|
||||||
|
|
||||||
const getPickerConfig = () => invoke<PickerConfiguration>('get_picker_config');
|
const getPickerConfig = () => invoke<PickerConfiguration>('get_picker_config');
|
||||||
const getScreenshotOfDisplays = () =>
|
const getScreenshotOfDisplays = () =>
|
||||||
|
Loading…
Reference in New Issue
Block a user