diff --git a/companion/lib/Surface/Controller.js b/companion/lib/Surface/Controller.js index 4cc0840ef5..b12e91d18f 100644 --- a/companion/lib/Surface/Controller.js +++ b/companion/lib/Surface/Controller.js @@ -672,6 +672,9 @@ class SurfaceController extends CoreBase { isConnected: !!surfaceHandler, displayName: getSurfaceName(config, id), location: null, + + size: config?.gridSize || null, + offset: { columns: config?.xOffset ?? 0, rows: config?.yOffset ?? 0 }, } if (surfaceHandler) { diff --git a/shared-lib/lib/Model/Surfaces.ts b/shared-lib/lib/Model/Surfaces.ts index 173195b170..6890e28b39 100644 --- a/shared-lib/lib/Model/Surfaces.ts +++ b/shared-lib/lib/Model/Surfaces.ts @@ -8,6 +8,11 @@ import type { } from '@companion-module/base' import { CompanionInputFieldTextInputExtended, EncodeIsVisible2 } from './Options.js' +export interface RowsAndColumns { + rows: number + columns: number +} + export interface ClientSurfaceItem { id: string type: string @@ -17,6 +22,9 @@ export interface ClientSurfaceItem { isConnected: boolean displayName: string location: string | null + + size: RowsAndColumns | null + offset: RowsAndColumns | null } export interface ClientDevicesListItem { diff --git a/webui/src/Buttons/ButtonGridPanel.tsx b/webui/src/Buttons/ButtonGridPanel.tsx index 6739458f46..29d103371c 100644 --- a/webui/src/Buttons/ButtonGridPanel.tsx +++ b/webui/src/Buttons/ButtonGridPanel.tsx @@ -26,6 +26,7 @@ import { observer } from 'mobx-react-lite' import { ButtonGridZoomControl } from './ButtonGridZoomControl.js' import { GridZoomController } from './GridZoom.js' import { CModalExt } from '../Components/CModalExt.js' +import { ButtonGridResizePrompt } from './ButtonGridResizePrompt.js' interface ButtonsGridPanelProps { pageNumber: number @@ -162,6 +163,8 @@ export const ButtonsGridPanel = observer(function ButtonsPage({ and what they should do when you press or click on them.

+ + diff --git a/webui/src/Buttons/ButtonGridResizePrompt.tsx b/webui/src/Buttons/ButtonGridResizePrompt.tsx new file mode 100644 index 0000000000..d8e60a6192 --- /dev/null +++ b/webui/src/Buttons/ButtonGridResizePrompt.tsx @@ -0,0 +1,35 @@ +import { CAlert, CButton } from '@coreui/react' +import { observer } from 'mobx-react-lite' +import React, { useContext } from 'react' +import { RootAppStoreContext } from '../Stores/RootAppStore.js' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome' +import { faExpand } from '@fortawesome/free-solid-svg-icons' + +export const ButtonGridResizePrompt = observer(function ButtonGridResizePrompt(): React.ReactNode { + const { socket, surfaces, userConfig } = useContext(RootAppStoreContext) + + const overflowing = userConfig.properties && surfaces.getSurfacesOverflowingBounds(userConfig.properties.gridSize) + if (!overflowing || overflowing.surfaces.length === 0) return null + + const doAutoResize = () => { + if (!overflowing) return + socket.emit('set_userconfig_key', 'gridSize', overflowing.neededBounds) + } + + return ( + <> + + You have some surfaces which overflow the current grid bounds +
    + {overflowing.surfaces.map((s) => ( +
  • {s.displayName}
  • + ))} +
+ + +  Resize grid to fit + +
+ + ) +}) diff --git a/webui/src/Stores/SurfacesStore.tsx b/webui/src/Stores/SurfacesStore.tsx index 77d8d85577..3e10d45972 100644 --- a/webui/src/Stores/SurfacesStore.tsx +++ b/webui/src/Stores/SurfacesStore.tsx @@ -3,11 +3,13 @@ import type { OutboundSurfaceInfo, SurfacesUpdate, OutboundSurfacesUpdate, + ClientSurfaceItem, } from '@companion-app/shared/Model/Surfaces.js' import { action, observable, toJS } from 'mobx' import { assertNever } from '../util.js' import { applyPatch } from 'fast-json-patch' import { cloneDeep } from 'lodash-es' +import { UserConfigGridSize } from '@companion-app/shared/Model/UserConfigModel.js' export class SurfacesStore { readonly store = observable.map() @@ -79,12 +81,42 @@ export class SurfacesStore { public getOutboundStreamDeckSurface = (address: string, port: number): OutboundSurfaceInfo | undefined => { for (const surface of this.outboundSurfaces.values()) { - console.log('check', toJS(surface)) - if (surface.type === 'elgato' && surface.address === address && (surface.port ?? 5343) === port) { return surface } } return undefined } + + public getSurfacesOverflowingBounds = ( + bounds: UserConfigGridSize + ): { neededBounds: UserConfigGridSize; surfaces: ClientSurfaceItem[] } => { + const neededBounds: UserConfigGridSize = { ...bounds } + const overflowingSurfaces: ClientSurfaceItem[] = [] + + for (const group of this.store.values()) { + for (const surface of group.surfaces) { + if (!surface.size || !surface.offset) continue + + const minX = surface.offset.columns + const minY = surface.offset.rows + const maxX = minX + surface.size.columns - 1 + const maxY = minY + surface.size.rows - 1 + + if (minX < bounds.minColumn || minY < bounds.minRow || maxX > bounds.maxColumn || maxY > bounds.maxRow) { + overflowingSurfaces.push(surface) + + neededBounds.minColumn = Math.min(neededBounds.minColumn, minX) + neededBounds.maxColumn = Math.max(neededBounds.maxColumn, maxX) + neededBounds.minRow = Math.min(neededBounds.minRow, minY) + neededBounds.maxRow = Math.max(neededBounds.maxRow, maxY) + } + } + } + + return { + neededBounds, + surfaces: overflowingSurfaces, + } + } }