feat: GUI 控制的,LED 灯条颜色预览。

This commit is contained in:
2023-03-26 10:48:50 +08:00
parent 3ede04c31b
commit 1a3102257e
23 changed files with 1067 additions and 192 deletions

View File

@ -4,6 +4,8 @@ import { DisplayView } from './components/display-view';
import { DisplayListContainer } from './components/display-list-container';
import { displayStore, setDisplayStore } from './stores/display.store';
import { path } from '@tauri-apps/api';
import { LedStripConfig } from './models/led-strip-config';
import { setLedStripStore } from './stores/led-strip.store';
function App() {
createEffect(() => {
@ -12,6 +14,13 @@ function App() {
displays: JSON.parse(displays),
});
});
invoke<LedStripConfig[]>('read_led_strip_configs').then((strips) => {
console.log(strips);
setLedStripStore({
strips,
});
});
});
return (

View File

@ -1,7 +1,10 @@
import { Component, createMemo } from 'solid-js';
import { LedStripConfigOfBorders } from '../models/display-config';
import { DisplayInfo } from '../models/display-info.model';
import { displayStore } from '../stores/display.store';
import { ledStripStore } from '../stores/led-strip.store';
import { DisplayInfoPanel } from './display-info-panel';
import { LedStripPart } from './led-strip-part';
import { ScreenView } from './screen-view';
type DisplayViewProps = {
@ -19,6 +22,12 @@ export const DisplayView: Component<DisplayViewProps> = (props) => {
height: `${size().height}px`,
width: `${size().width}px`,
}));
const ledStripConfigs = createMemo(() => {
console.log('ledStripConfigs', ledStripStore.strips);
return ledStripStore.strips.filter((c) => c.display_id === props.display.id);
});
return (
<section
class="absolute bg-gray-300 grid grid-cols-[16px,auto,16px] grid-rows-[16px,auto,16px] overflow-hidden"
@ -35,10 +44,22 @@ export const DisplayView: Component<DisplayViewProps> = (props) => {
display={props.display}
class="absolute bg-slate-50/10 top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 rounded backdrop-blur w-1/3 min-w-[300px] text-black"
/>
<div class="row-start-1 col-start-2">Test</div>
<div class="row-start-2 col-start-1">Test</div>
<div class="row-start-2 col-start-3">Test</div>
<div class="row-start-3 col-start-2">Test</div>
<LedStripPart
class="row-start-1 col-start-2 flex-row"
config={ledStripConfigs().find((c) => c.border === 'Top')}
/>
<LedStripPart
class="row-start-2 col-start-1 flex-col"
config={ledStripConfigs().find((c) => c.border === 'Left')}
/>
<LedStripPart
class="row-start-2 col-start-3 flex-col"
config={ledStripConfigs().find((c) => c.border === 'Right')}
/>
<LedStripPart
class="row-start-3 col-start-2 flex-row"
config={ledStripConfigs().find((c) => c.border === 'Bottom')}
/>
</section>
);
};

View File

@ -0,0 +1,130 @@
import { invoke } from '@tauri-apps/api';
import { listen } from '@tauri-apps/api/event';
import {
Component,
createEffect,
createMemo,
createSignal,
For,
JSX,
on,
onCleanup,
splitProps,
} from 'solid-js';
import { borders } from '../constants/border';
import { LedStripConfig } from '../models/led-strip-config';
type LedStripPartProps = {
config?: LedStripConfig | null;
} & JSX.HTMLAttributes<HTMLElement>;
type PixelProps = {
color: string;
};
async function subscribeScreenshotUpdate(displayId: number) {
await invoke('subscribe_encoded_screenshot_updated', {
displayId,
});
}
export const Pixel: Component<PixelProps> = (props) => {
const style = createMemo(() => ({
background: props.color,
}));
return (
<div
class="inline-block flex-shrink w-2 h-2 aspect-square rounded-full border border-black"
style={style()}
title={props.color}
/>
);
};
export const LedStripPart: Component<LedStripPartProps> = (props) => {
const [localProps, rootProps] = splitProps(props, ['config']);
const [ledSamplePoints, setLedSamplePoints] = createSignal();
const [colors, setColors] = createSignal<string[]>([]);
createEffect(() => {
const samplePoints = ledSamplePoints();
if (!localProps.config || !samplePoints) {
return;
}
let pendingCount = 0;
const unlisten = listen<{
base64_image: string;
display_id: number;
height: number;
width: number;
}>('encoded-screenshot-updated', (event) => {
if (event.payload.display_id !== localProps.config!.display_id) {
return;
}
if (pendingCount >= 1) {
return;
}
pendingCount++;
console.log({
samplePoints,
displayId: event.payload.display_id,
border: localProps.config!.border,
});
invoke<string[]>('get_one_edge_colors', {
samplePoints,
displayId: event.payload.display_id,
})
.then((colors) => {
setColors(colors);
})
.finally(() => {
pendingCount--;
});
});
subscribeScreenshotUpdate(localProps.config.display_id);
onCleanup(() => {
unlisten.then((unlisten) => unlisten());
});
});
createEffect(() => {
if (localProps.config) {
invoke('get_led_strips_sample_points', {
config: localProps.config,
}).then((points) => {
console.log({ points });
setLedSamplePoints(points);
});
}
});
const pixels = createMemo(() => {
const _colors = colors();
if (_colors) {
return <For each={_colors}>{(item) => <Pixel color={item} />}</For>;
} else if (localProps.config) {
return null;
return (
<For each={new Array(localProps.config.len).fill(undefined)}>
{() => <Pixel color="transparent" />}
</For>
);
}
});
return (
<section
{...rootProps}
class={
'bg-yellow-50 flex flex-nowrap justify-around items-center overflow-hidden' +
rootProps.class
}
>
{pixels()}
</section>
);
};

View File

@ -65,7 +65,7 @@ export const ScreenView: Component<ScreenViewProps> = (props) => {
};
const draw = (cached: boolean = false) => {
const { drawX, drawY, drawWidth, drawHeight } = drawInfo();
const { drawX, drawY } = drawInfo();
let _ctx = ctx();
let raw = imageData();
@ -82,6 +82,7 @@ export const ScreenView: Component<ScreenViewProps> = (props) => {
};
createEffect(() => {
let pendingCount = 0;
const unlisten = listen<{
base64_image: string;
display_id: number;
@ -94,12 +95,15 @@ export const ScreenView: Component<ScreenViewProps> = (props) => {
`displays/${localProps.displayId}?width=${drawWidth}&height=${drawHeight}`,
'ambient-light',
);
if (pendingCount >= 1) {
return;
}
pendingCount++;
fetch(url, {
mode: 'cors',
})
.then((res) => res.body?.getReader().read())
.then((buffer) => {
// console.log(buffer?.value?.length);
if (buffer?.value) {
setImageData({
buffer: new Uint8ClampedArray(buffer?.value),
@ -110,6 +114,9 @@ export const ScreenView: Component<ScreenViewProps> = (props) => {
setImageData(null);
}
draw();
})
.finally(() => {
pendingCount--;
});
}

2
src/constants/border.ts Normal file
View File

@ -0,0 +1,2 @@
export const borders = ['Top', 'Right', 'Bottom', 'Left'] as const;
export type Borders = typeof borders[number];

View File

@ -0,0 +1,16 @@
import { Borders } from '../constants/border';
import { LedStripConfig } from './led-strip-config';
export class LedStripConfigOfBorders implements Record<Borders, LedStripConfig | null> {
constructor(
public top: LedStripConfig | null = null,
public bottom: LedStripConfig | null = null,
public left: LedStripConfig | null = null,
public right: LedStripConfig | null = null,
) {}
}
export class DisplayConfig {
led_strip_of_borders = new LedStripConfigOfBorders();
constructor(public id: number) {}
}

View File

@ -0,0 +1,10 @@
import { Borders } from '../constants/border';
export class LedStripConfig {
constructor(
public readonly display_id: number,
public readonly border: Borders,
public start_pos: number,
public len: number,
) {}
}

View File

@ -0,0 +1,8 @@
import { DisplayConfig } from './display-config';
export class PickerConfiguration {
constructor(
public display_configs: DisplayConfig[] = [],
public config_version: number = 1,
) {}
}

1
src/models/pixel-rgb.ts Normal file
View File

@ -0,0 +1 @@
export type PixelRgb = [number, number, number];

View File

@ -0,0 +1,12 @@
import { DisplayConfig } from './display-config';
export class ScreenshotDto {
encode_image!: string;
config!: DisplayConfig;
colors!: {
top: Uint8Array;
bottom: Uint8Array;
left: Uint8Array;
right: Uint8Array;
};
}

View File

@ -0,0 +1,8 @@
import { createStore } from 'solid-js/store';
import { DisplayConfig } from '../models/display-config';
import { LedStripConfig } from '../models/led-strip-config';
export const [ledStripStore, setLedStripStore] = createStore({
displays: new Array<DisplayConfig>(),
strips: new Array<LedStripConfig>(),
});