diff --git a/js/actionCreators/index.ts b/js/actionCreators/index.ts index 77a1552c..f51b5308 100644 --- a/js/actionCreators/index.ts +++ b/js/actionCreators/index.ts @@ -20,7 +20,8 @@ export { toggleWindow, updateWindowPositions, toggleMainWindowShadeMode, - windowsHaveBeenCentered + windowsHaveBeenCentered, + centerWindowsIfNeeded } from "./windows"; export { play, diff --git a/js/actionCreators/windows.ts b/js/actionCreators/windows.ts index f95a1805..2e555052 100644 --- a/js/actionCreators/windows.ts +++ b/js/actionCreators/windows.ts @@ -1,7 +1,10 @@ import { getWindowGraph, getWindowSizes, - getWindowPositions + getWindowPositions, + getWindowsInfo, + getWindowOpen, + getGenWindows } from "../selectors"; import { objectMap } from "../utils"; @@ -20,6 +23,9 @@ import { getPositionDiff, SizeDiff } from "../resizeUtils"; import { applyDiff } from "../snapUtils"; import { Action, Dispatchable, WindowId, WindowPositions } from "../types"; +import { WINDOW_HEIGHT, WINDOW_WIDTH } from "../constants"; +import { calculateBoundingBox } from "../utils"; + // Dispatch an action and, if needed rearrange the windows to preserve // the existing edge relationship. // @@ -111,6 +117,70 @@ export function updateWindowPositions( return { type: UPDATE_WINDOW_POSITIONS, positions, center }; } -export function windowsHaveBeenCentered() { +export function windowsHaveBeenCentered(): Dispatchable { return { type: WINDOWS_HAVE_BEEN_CENTERED }; } + +export function centerWindowsIfNeeded(container: HTMLElement): Dispatchable { + return (dispatch, getState) => { + const state = getState(); + const { centerRequested } = state.windows; + if (!centerRequested) { + return; + } + const genWindows = getGenWindows(state); + const windowsInfo = getWindowsInfo(state); + const getOpen = getWindowOpen(state); + const rect = container.getBoundingClientRect(); + + const offsetLeft = rect.left + window.scrollX; + const offsetTop = rect.top + window.scrollY; + const width = container.scrollWidth; + const height = container.scrollHeight; + + if (windowsInfo.some(w => w.x == null || w.y == null)) { + // Some windows do not have an initial position, so we'll come up + // with your own layout. + const windowPositions: WindowPositions = {}; + const keys: string[] = Object.keys(genWindows).filter( + windowId => genWindows[windowId].open + ); + const totalHeight = keys.length * WINDOW_HEIGHT; + const globalOffsetLeft = Math.max(0, width / 2 - WINDOW_WIDTH / 2); + const globalOffsetTop = Math.max(0, height / 2 - totalHeight / 2); + keys.forEach((key, i) => { + const offset = WINDOW_HEIGHT * i; + windowPositions[key] = { + x: Math.ceil(offsetLeft + globalOffsetLeft), + y: Math.ceil(offsetTop + (globalOffsetTop + offset)) + }; + }); + dispatch(updateWindowPositions(windowPositions, false)); + } else { + // A layout has been suplied. We will compute the bounding box and + // center the given layout. + const bounding = calculateBoundingBox( + windowsInfo.filter(w => getOpen(w.key)) + ); + + const boxHeight = bounding.bottom - bounding.top; + const boxWidth = bounding.right - bounding.left; + + const move = { + x: Math.ceil(offsetLeft - bounding.left + (width - boxWidth) / 2), + y: Math.ceil(offsetTop - bounding.top + (height - boxHeight) / 2) + }; + + const newPositions = windowsInfo.reduce( + (pos, w) => ({ + ...pos, + [w.key]: { x: move.x + w.x, y: move.y + w.y } + }), + {} + ); + + dispatch(updateWindowPositions(newPositions, false)); + } + dispatch(windowsHaveBeenCentered()); + }; +} diff --git a/js/components/WindowManager.js b/js/components/WindowManager.js index 3189e11b..d9c96a0e 100644 --- a/js/components/WindowManager.js +++ b/js/components/WindowManager.js @@ -11,19 +11,12 @@ import { applyDiff, applyMultipleDiffs } from "../snapUtils"; -import { - getWindowsInfo, - getWindowHidden, - getWindowOpen, - getCenterRequested -} from "../selectors"; +import { getWindowsInfo, getWindowHidden, getWindowOpen } from "../selectors"; import { updateWindowPositions, - windowsHaveBeenCentered + windowsHaveBeenCentered, + centerWindowsIfNeeded } from "../actionCreators"; -import { WINDOW_HEIGHT, WINDOW_WIDTH } from "../constants"; -import { calculateBoundingBox } from "../utils"; - const abuts = (a, b) => { // TODO: This is kinda a hack. They should really be touching, not just within snapping distance. // Also, overlapping should not count. @@ -33,65 +26,9 @@ const abuts = (a, b) => { class WindowManager extends React.Component { componentDidMount() { - this.centerWindowsIfNeeded(); + this.props.centerWindowsIfNeeded(this.props.container); } - centerWindowsIfNeeded = () => { - const { container, centerRequested } = this.props; - if (!centerRequested) { - return; - } - - const rect = container.getBoundingClientRect(); - const offsetLeft = rect.left + window.scrollX; - const offsetTop = rect.top + window.scrollY; - const width = container.scrollWidth; - const height = container.scrollHeight; - - if (this.props.windowsInfo.some(w => w.x == null || w.y == null)) { - // Some windows do not have an initial position, so we'll come up - // with your own layout. - const windowPositions = {}; - const keys = this.windowKeys(); - const totalHeight = keys.length * WINDOW_HEIGHT; - const globalOffsetLeft = Math.max(0, width / 2 - WINDOW_WIDTH / 2); - const globalOffsetTop = Math.max(0, height / 2 - totalHeight / 2); - keys.forEach((key, i) => { - const offset = WINDOW_HEIGHT * i; - windowPositions[key] = { - x: Math.ceil(offsetLeft + globalOffsetLeft), - y: Math.ceil(offsetTop + (globalOffsetTop + offset)) - }; - }); - this.props.updateWindowPositions(windowPositions, false); - } else { - // A layout has been suplied. We will compute the bounding box and - // center the given layout. - const bounding = calculateBoundingBox( - this.props.windowsInfo.filter(w => this.props.getWindowOpen(w.key)) - ); - - const boxHeight = bounding.bottom - bounding.top; - const boxWidth = bounding.right - bounding.left; - - const move = { - x: Math.ceil(offsetLeft - bounding.left + (width - boxWidth) / 2), - y: Math.ceil(offsetTop - bounding.top + (height - boxHeight) / 2) - }; - - const newPositions = this.props.windowsInfo.reduce( - (pos, w) => ({ - ...pos, - [w.key]: { x: move.x + w.x, y: move.y + w.y } - }), - {} - ); - - this.props.updateWindowPositions(newPositions, false); - } - this.props.windowsHaveBeenCentered(); - }; - movingAndStationaryNodes(key) { const windows = this.props.windowsInfo.filter( w => @@ -183,14 +120,6 @@ class WindowManager extends React.Component { window.addEventListener("mousemove", handleMouseMove); }; - // Keys for the visible windows - windowKeys() { - // TODO: Iterables can probably do this better. - return Object.keys(this.props.windows).filter( - key => !!this.props.windows[key] - ); - } - render() { const style = { position: "absolute", @@ -222,15 +151,16 @@ WindowManager.propTypes = { const mapStateToProps = state => ({ windowsInfo: getWindowsInfo(state), getWindowHidden: getWindowHidden(state), - getWindowOpen: getWindowOpen(state), - centerRequested: getCenterRequested(state) + getWindowOpen: getWindowOpen(state) }); const mapDispatchToProps = dispatch => { return { updateWindowPositions: (positions, centered) => dispatch(updateWindowPositions(positions, centered)), - windowsHaveBeenCentered: () => dispatch(windowsHaveBeenCentered()) + windowsHaveBeenCentered: () => dispatch(windowsHaveBeenCentered()), + centerWindowsIfNeeded: container => + dispatch(centerWindowsIfNeeded(container)) }; }; diff --git a/js/types.ts b/js/types.ts index ec5f70ff..cf8668a0 100644 --- a/js/types.ts +++ b/js/types.ts @@ -342,6 +342,7 @@ export type Action = | { type: "MINIMIZE_WINAMP"; } + | { type: "WINDOWS_HAVE_BEEN_CENTERED" } | { type: "CLOSE_REQUESTED"; cancel: () => void; diff --git a/js/utils.ts b/js/utils.ts index 5c2a4ef5..2fa24fbd 100644 --- a/js/utils.ts +++ b/js/utils.ts @@ -1,4 +1,5 @@ import { DEFAULT_SKIN } from "./constants"; +import { WindowInfo } from "./types"; interface Time { minutesFirstDigit: string; @@ -318,16 +319,7 @@ export function objectFilter( }, {}); } -interface Window { - left: number; - top: number; - x: number; - y: number; - width: number; - height: number; -} - -export const calculateBoundingBox = (windows: Window[]) => +export const calculateBoundingBox = (windows: WindowInfo[]) => windows .map(w => ({ left: w.x,