diff --git a/packages/streamwall-control-ui/src/index.tsx b/packages/streamwall-control-ui/src/index.tsx index b7051fe..9eaa5a4 100644 --- a/packages/streamwall-control-ui/src/index.tsx +++ b/packages/streamwall-control-ui/src/index.tsx @@ -256,10 +256,11 @@ export function ControlUI({ role, } = connection const { - gridCount, + cols, + rows, width: windowWidth, height: windowHeight, - } = config ?? { gridCount: null, width: null, height: null } + } = config ?? { cols: null, rows: null, width: null, height: null } const [showDebug, setShowDebug] = useState(false) const handleChangeShowDebug = useCallback< @@ -311,20 +312,24 @@ export function ControlUI({ const [hoveringIdx, setHoveringIdx] = useState() const updateHoveringIdx = useCallback( (ev: MouseEvent) => { - if (gridCount == null || !(ev.currentTarget instanceof HTMLElement)) { + if ( + cols == null || + rows == null || + !(ev.currentTarget instanceof HTMLElement) + ) { return } const { width, height, left, top } = ev.currentTarget.getBoundingClientRect() const x = Math.floor(ev.clientX - left) const y = Math.floor(ev.clientY - top) - const spaceWidth = width / gridCount - const spaceHeight = height / gridCount + const spaceWidth = width / cols + const spaceHeight = height / rows const idx = - Math.floor(y / spaceHeight) * gridCount + Math.floor(x / spaceWidth) + Math.floor(y / spaceHeight) * cols + Math.floor(x / spaceWidth) setHoveringIdx(idx) }, - [setHoveringIdx, gridCount], + [setHoveringIdx, cols, rows], ) const [dragStart, setDragStart] = useState() const handleDragStart = useCallback( @@ -347,14 +352,19 @@ export function ControlUI({ ) useLayoutEffect(() => { function endDrag() { - if (dragStart == null || gridCount == null || hoveringIdx == null) { + if ( + dragStart == null || + cols == null || + rows == null || + hoveringIdx == null + ) { return } stateDoc.transact(() => { const viewsState = stateDoc.getMap>('views') const streamId = viewsState.get(String(dragStart))?.get('streamId') - for (let idx = 0; idx < gridCount ** 2; idx++) { - if (idxInBox(gridCount, dragStart, hoveringIdx, idx)) { + for (let idx = 0; idx < cols * rows; idx++) { + if (idxInBox(cols, dragStart, hoveringIdx, idx)) { viewsState.get(String(idx))?.set('streamId', streamId) } } @@ -462,7 +472,7 @@ export function ControlUI({ const handleClickId = useCallback( (streamId: string) => { - if (gridCount == null || sharedState == null) { + if (cols == null || rows == null || sharedState == null) { return } @@ -477,7 +487,7 @@ export function ControlUI({ return } - const availableIdx = range(gridCount * gridCount).find( + const availableIdx = range(cols * rows).find( (i) => !sharedState.views[i].streamId, ) if (availableIdx === undefined) { @@ -485,7 +495,7 @@ export function ControlUI({ } handleSetView(availableIdx, streamId) }, - [gridCount, sharedState, focusedInputIdx], + [cols, rows, sharedState, focusedInputIdx], ) const handleChangeCustomStream = useCallback( @@ -660,7 +670,7 @@ export function ControlUI({ /> )} - {gridCount && ( + {cols != null && rows != null && ( - {range(0, gridCount).map((y) => - range(0, gridCount).map((x) => { - const idx = gridCount * y + x + {range(0, rows).map((y) => + range(0, cols).map((x) => { + const idx = cols * y + x const { streamId } = sharedState?.views?.[idx] ?? {} const isDragHighlighted = dragStart != null && hoveringIdx != null && - idxInBox(gridCount, dragStart, hoveringIdx, idx) + idxInBox(cols, dragStart, hoveringIdx, idx) return ( export function boxesFromViewContentMap( - width: number, - height: number, + cols: number, + rows: number, viewContentMap: ViewContentMap, ) { const boxes = [] @@ -28,7 +28,7 @@ export function boxesFromViewContentMap( y: number, content: ViewContent | undefined, ) { - const checkIdx = width * y + x + const checkIdx = cols * y + x return ( !visited.has(checkIdx) && isEqual(viewContentMap.get(String(checkIdx)), content) @@ -36,28 +36,28 @@ export function boxesFromViewContentMap( } function findLargestBox(x: number, y: number) { - const idx = width * y + x + const idx = cols * y + x const spaces = [idx] const content = viewContentMap.get(String(idx)) let maxY - for (maxY = y + 1; maxY < height; maxY++) { + for (maxY = y + 1; maxY < rows; maxY++) { if (!isPosContent(x, maxY, content)) { break } - spaces.push(width * maxY + x) + spaces.push(cols * maxY + x) } let cx = x let cy = y - scan: for (cx = x + 1; cx < width; cx++) { + scan: for (cx = x + 1; cx < cols; cx++) { for (cy = y; cy < maxY; cy++) { if (!isPosContent(cx, cy, content)) { break scan } } for (let cy = y; cy < maxY; cy++) { - spaces.push(width * cy + cx) + spaces.push(cols * cy + cx) } } const w = cx - x @@ -66,9 +66,9 @@ export function boxesFromViewContentMap( return { content, x, y, w, h, spaces } } - for (let y = 0; y < width; y++) { - for (let x = 0; x < height; x++) { - const idx = width * y + x + for (let y = 0; y < rows; y++) { + for (let x = 0; x < cols; x++) { + const idx = cols * y + x if (visited.has(idx) || viewContentMap.get(String(idx)) === undefined) { continue } @@ -84,21 +84,21 @@ export function boxesFromViewContentMap( return boxes } -export function idxToCoords(gridCount: number, idx: number) { - const x = idx % gridCount - const y = Math.floor(idx / gridCount) +export function idxToCoords(cols: number, idx: number) { + const x = idx % cols + const y = Math.floor(idx / cols) return { x, y } } export function idxInBox( - gridCount: number, + cols: number, start: number, end: number, idx: number, ) { - const { x: startX, y: startY } = idxToCoords(gridCount, start) - const { x: endX, y: endY } = idxToCoords(gridCount, end) - const { x, y } = idxToCoords(gridCount, idx) + const { x: startX, y: startY } = idxToCoords(cols, start) + const { x: endX, y: endY } = idxToCoords(cols, end) + const { x, y } = idxToCoords(cols, idx) const lowX = Math.min(startX, endX) const highX = Math.max(startX, endX) const lowY = Math.min(startY, endY) diff --git a/packages/streamwall-shared/src/types.ts b/packages/streamwall-shared/src/types.ts index 617afdb..016c077 100644 --- a/packages/streamwall-shared/src/types.ts +++ b/packages/streamwall-shared/src/types.ts @@ -2,7 +2,8 @@ import type { ViewContent, ViewPos } from './geometry.ts' import type { StreamwallRole } from './roles.ts' export interface StreamWindowConfig { - gridCount: number + cols: number + rows: number width: number height: number x?: number diff --git a/packages/streamwall/src/main/StreamWindow.ts b/packages/streamwall/src/main/StreamWindow.ts index 21362b8..c024f14 100644 --- a/packages/streamwall/src/main/StreamWindow.ts +++ b/packages/streamwall/src/main/StreamWindow.ts @@ -198,11 +198,11 @@ export default class StreamWindow extends EventEmitter { } setViews(viewContentMap: ViewContentMap, streams: StreamList) { - const { width, height, gridCount } = this.config - const spaceWidth = Math.floor(width / gridCount) - const spaceHeight = Math.floor(height / gridCount) + const { width, height, cols, rows } = this.config + const spaceWidth = Math.floor(width / cols) + const spaceHeight = Math.floor(height / rows) const { win, views } = this - const boxes = boxesFromViewContentMap(gridCount, gridCount, viewContentMap) + const boxes = boxesFromViewContentMap(cols, rows, viewContentMap) const remainingBoxes = new Set(boxes) const unusedViews = new Set(views.values()) const viewsToDisplay = [] diff --git a/packages/streamwall/src/main/index.ts b/packages/streamwall/src/main/index.ts index cb835c3..91de3e9 100644 --- a/packages/streamwall/src/main/index.ts +++ b/packages/streamwall/src/main/index.ts @@ -34,7 +34,8 @@ const SENTRY_DSN = export interface StreamwallConfig { help: boolean grid: { - count: number + cols: number + rows: number } window: { x?: number @@ -97,8 +98,12 @@ function parseArgs(): StreamwallConfig { .config('config', (configPath) => { return TOML.parse(fs.readFileSync(configPath, 'utf-8')) }) - .group(['grid.count'], 'Grid dimensions') - .option('grid.count', { + .group(['grid.cols', 'grid.rows'], 'Grid dimensions') + .option('grid.cols', { + number: true, + default: 3, + }) + .option('grid.rows', { number: true, default: 3, }) @@ -267,7 +272,8 @@ async function main(argv: ReturnType) { const overlayStreamData = new LocalStreamData() const streamWindowConfig = { - gridCount: argv.grid.count, + cols: argv.grid.cols, + rows: argv.grid.rows, width: argv.window.width, height: argv.window.height, x: argv.window.x, @@ -337,7 +343,7 @@ async function main(argv: ReturnType) { ) stateDoc.transact(() => { - for (let i = 0; i < argv.grid.count ** 2; i++) { + for (let i = 0; i < argv.grid.cols * argv.grid.rows; i++) { if (viewsState.has(String(i))) { continue }