mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-21 00:59:29 +00:00
Add initial approach of recovering from bad window positions
This commit is contained in:
parent
40e31f0577
commit
1791babf1a
10 changed files with 160 additions and 63 deletions
|
|
@ -4,6 +4,9 @@
|
|||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Prevent accidental highlighting */
|
||||
|
|
|
|||
|
|
@ -5,9 +5,11 @@ import {
|
|||
CLOSE_REQUESTED,
|
||||
MINIMIZE_WINAMP,
|
||||
SET_FOCUS,
|
||||
UNSET_FOCUS
|
||||
UNSET_FOCUS,
|
||||
LOAD_SERIALIZED_STATE
|
||||
} from "../actionTypes";
|
||||
import { Dispatchable } from "../types";
|
||||
import { Dispatchable, SerializedStateV1 } from "../types";
|
||||
import { ensureWindowsAreOnScreen } from "./windows";
|
||||
|
||||
export {
|
||||
toggleDoubleSizeMode,
|
||||
|
|
@ -21,7 +23,8 @@ export {
|
|||
updateWindowPositions,
|
||||
toggleMainWindowShadeMode,
|
||||
windowsHaveBeenCentered,
|
||||
centerWindowsIfNeeded
|
||||
centerWindowsIfNeeded,
|
||||
ensureWindowsAreOnScreen
|
||||
} from "./windows";
|
||||
export {
|
||||
play,
|
||||
|
|
@ -110,3 +113,12 @@ export function setFocus(input: string): Dispatchable {
|
|||
export function unsetFocus(): Dispatchable {
|
||||
return { type: UNSET_FOCUS };
|
||||
}
|
||||
|
||||
export function loadSerializedState(
|
||||
serializedState: SerializedStateV1
|
||||
): Dispatchable {
|
||||
return dispatch => {
|
||||
dispatch({ type: LOAD_SERIALIZED_STATE, serializedState });
|
||||
dispatch(ensureWindowsAreOnScreen());
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,13 +1,6 @@
|
|||
import {
|
||||
getWindowGraph,
|
||||
getWindowSizes,
|
||||
getWindowPositions,
|
||||
getWindowsInfo,
|
||||
getWindowOpen,
|
||||
getGenWindows
|
||||
} from "../selectors";
|
||||
import * as Selectors from "../selectors";
|
||||
|
||||
import { objectMap } from "../utils";
|
||||
import * as Utils from "../utils";
|
||||
import {
|
||||
UPDATE_WINDOW_POSITIONS,
|
||||
TOGGLE_DOUBLESIZE_MODE,
|
||||
|
|
@ -16,7 +9,8 @@ import {
|
|||
CLOSE_WINDOW,
|
||||
TOGGLE_WINDOW_SHADE_MODE,
|
||||
SET_WINDOW_VISIBILITY,
|
||||
WINDOWS_HAVE_BEEN_CENTERED
|
||||
WINDOWS_HAVE_BEEN_CENTERED,
|
||||
RESET_WINDOW_LAYOUT
|
||||
} from "../actionTypes";
|
||||
|
||||
import { getPositionDiff, SizeDiff } from "../resizeUtils";
|
||||
|
|
@ -35,12 +29,12 @@ import { calculateBoundingBox } from "../utils";
|
|||
function withWindowGraphIntegrity(action: Action): Dispatchable {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const graph = getWindowGraph(state);
|
||||
const originalSizes = getWindowSizes(state);
|
||||
const graph = Selectors.getWindowGraph(state);
|
||||
const originalSizes = Selectors.getWindowSizes(state);
|
||||
|
||||
dispatch(action);
|
||||
|
||||
const newSizes = getWindowSizes(getState());
|
||||
const newSizes = Selectors.getWindowSizes(getState());
|
||||
const sizeDiff: SizeDiff = {};
|
||||
for (const window of Object.keys(newSizes)) {
|
||||
const original = originalSizes[window];
|
||||
|
|
@ -52,9 +46,9 @@ function withWindowGraphIntegrity(action: Action): Dispatchable {
|
|||
}
|
||||
|
||||
const positionDiff = getPositionDiff(graph, sizeDiff);
|
||||
const windowPositions = getWindowPositions(state);
|
||||
const windowPositions = Selectors.getWindowPositions(state);
|
||||
|
||||
const newPositions = objectMap(windowPositions, (position, key) =>
|
||||
const newPositions = Utils.objectMap(windowPositions, (position, key) =>
|
||||
applyDiff(position, positionDiff[key])
|
||||
);
|
||||
|
||||
|
|
@ -121,6 +115,10 @@ export function windowsHaveBeenCentered(): Dispatchable {
|
|||
return { type: WINDOWS_HAVE_BEEN_CENTERED };
|
||||
}
|
||||
|
||||
export function resetWindowLayout(): Dispatchable {
|
||||
return { type: RESET_WINDOW_LAYOUT };
|
||||
}
|
||||
|
||||
export function centerWindowsIfNeeded(container: HTMLElement): Dispatchable {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
|
|
@ -128,9 +126,10 @@ export function centerWindowsIfNeeded(container: HTMLElement): Dispatchable {
|
|||
if (!centerRequested) {
|
||||
return;
|
||||
}
|
||||
const genWindows = getGenWindows(state);
|
||||
const windowsInfo = getWindowsInfo(state);
|
||||
const getOpen = getWindowOpen(state);
|
||||
const genWindows = Selectors.getGenWindows(state);
|
||||
const windowsInfo = Selectors.getWindowsInfo(state);
|
||||
const getOpen = Selectors.getWindowOpen(state);
|
||||
const getPixelSize = Selectors.getWindowPixelSize(state);
|
||||
const rect = container.getBoundingClientRect();
|
||||
|
||||
const offsetLeft = rect.left + window.scrollX;
|
||||
|
|
@ -145,15 +144,18 @@ export function centerWindowsIfNeeded(container: HTMLElement): Dispatchable {
|
|||
const keys: string[] = Object.keys(genWindows).filter(
|
||||
windowId => genWindows[windowId].open
|
||||
);
|
||||
const totalHeight = keys.length * WINDOW_HEIGHT;
|
||||
const totalHeight = keys.reduce((height, key) => {
|
||||
return height + getPixelSize(key).height;
|
||||
}, 0);
|
||||
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;
|
||||
let offset = 0;
|
||||
keys.forEach(key => {
|
||||
windowPositions[key] = {
|
||||
x: Math.ceil(offsetLeft + globalOffsetLeft),
|
||||
y: Math.ceil(offsetTop + (globalOffsetTop + offset))
|
||||
};
|
||||
offset += getPixelSize(key).height;
|
||||
});
|
||||
dispatch(updateWindowPositions(windowPositions, false));
|
||||
} else {
|
||||
|
|
@ -184,3 +186,61 @@ export function centerWindowsIfNeeded(container: HTMLElement): Dispatchable {
|
|||
dispatch(windowsHaveBeenCentered());
|
||||
};
|
||||
}
|
||||
|
||||
export function ensureWindowsAreOnScreen(): Dispatchable {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
|
||||
const windowsInfo = Selectors.getWindowsInfo(state);
|
||||
const getOpen = Selectors.getWindowOpen(state);
|
||||
const { height, width } = Utils.getWindowSize();
|
||||
const bounding = calculateBoundingBox(
|
||||
windowsInfo.filter(w => getOpen(w.key))
|
||||
);
|
||||
const positions = Selectors.getWindowPositions(state);
|
||||
|
||||
// Are we good?
|
||||
if (
|
||||
bounding.left >= 0 &&
|
||||
bounding.top >= 0 &&
|
||||
bounding.right <= width &&
|
||||
bounding.bottom <= height
|
||||
) {
|
||||
// My work here is done.
|
||||
return;
|
||||
}
|
||||
|
||||
const boundingHeight = bounding.bottom - bounding.top;
|
||||
const boundingWidth = bounding.right - bounding.left;
|
||||
|
||||
// Could we simply shift all the windows by a constant offset?
|
||||
if (boundingWidth <= width && boundingHeight <= height) {
|
||||
let moveY = 0;
|
||||
let moveX = 0;
|
||||
if (bounding.top <= 0) {
|
||||
moveY = bounding.top;
|
||||
} else if (bounding.bottom > height) {
|
||||
moveY = bounding.bottom - height;
|
||||
}
|
||||
|
||||
if (bounding.left <= 0) {
|
||||
moveX = bounding.left;
|
||||
} else if (bounding.right > width) {
|
||||
moveX = bounding.right - width;
|
||||
}
|
||||
|
||||
const newPositions = Utils.objectMap(positions, position => ({
|
||||
x: position.x - moveX,
|
||||
y: position.y - moveY
|
||||
}));
|
||||
|
||||
dispatch(updateWindowPositions(newPositions, false));
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Try moving the individual groups to try to fit them in
|
||||
|
||||
// I give up. Just reset everything.
|
||||
dispatch(resetWindowLayout());
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,3 +73,4 @@ export const LOADING = "LOADING";
|
|||
export const CLOSE_REQUESTED = "CLOSE_REQUESTED";
|
||||
export const LOAD_SERIALIZED_STATE = "LOAD_SERIALIZED_STATE";
|
||||
export const WINDOWS_HAVE_BEEN_CENTERED = "WINDOWS_HAVE_BEEN_CENTERED";
|
||||
export const RESET_WINDOW_LAYOUT = "RESET_WINDOW_LAYOUT";
|
||||
|
|
|
|||
|
|
@ -11,7 +11,8 @@ import {
|
|||
applyDiff,
|
||||
applyMultipleDiffs
|
||||
} from "../snapUtils";
|
||||
import { getWindowsInfo, getWindowHidden, getWindowOpen } from "../selectors";
|
||||
import { getWindowSize } from "../utils";
|
||||
import * as Selectors from "../selectors";
|
||||
import {
|
||||
updateWindowPositions,
|
||||
windowsHaveBeenCentered,
|
||||
|
|
@ -29,6 +30,10 @@ class WindowManager extends React.Component {
|
|||
this.props.centerWindowsIfNeeded(this.props.container);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.props.centerWindowsIfNeeded(this.props.container);
|
||||
}
|
||||
|
||||
movingAndStationaryNodes(key) {
|
||||
const windows = this.props.windowsInfo.filter(
|
||||
w =>
|
||||
|
|
@ -60,24 +65,7 @@ class WindowManager extends React.Component {
|
|||
|
||||
const mouseStart = { x: e.clientX, y: e.clientY };
|
||||
// Aparently this is crazy across browsers.
|
||||
const browserSize = {
|
||||
width: Math.max(
|
||||
document.body.scrollWidth,
|
||||
document.documentElement.scrollWidth,
|
||||
document.body.offsetWidth,
|
||||
document.documentElement.offsetWidth,
|
||||
document.body.clientWidth,
|
||||
document.documentElement.clientWidth
|
||||
),
|
||||
height: Math.max(
|
||||
document.body.scrollHeight,
|
||||
document.documentElement.scrollHeight,
|
||||
document.body.offsetHeight,
|
||||
document.documentElement.offsetHeight,
|
||||
document.body.clientHeight,
|
||||
document.documentElement.clientHeight
|
||||
)
|
||||
};
|
||||
const browserSize = getWindowSize();
|
||||
|
||||
const box = boundingBox(moving);
|
||||
|
||||
|
|
@ -149,9 +137,9 @@ WindowManager.propTypes = {
|
|||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
windowsInfo: getWindowsInfo(state),
|
||||
getWindowHidden: getWindowHidden(state),
|
||||
getWindowOpen: getWindowOpen(state)
|
||||
windowsInfo: Selectors.getWindowsInfo(state),
|
||||
getWindowHidden: Selectors.getWindowHidden(state),
|
||||
getWindowOpen: Selectors.getWindowOpen(state)
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => {
|
||||
|
|
|
|||
|
|
@ -9,9 +9,10 @@ import {
|
|||
UPDATE_WINDOW_POSITIONS,
|
||||
WINDOW_SIZE_CHANGED,
|
||||
TOGGLE_WINDOW_SHADE_MODE,
|
||||
LOAD_SERIALIZED_STATE
|
||||
LOAD_SERIALIZED_STATE,
|
||||
RESET_WINDOW_LAYOUT
|
||||
} from "../actionTypes";
|
||||
import { objectMap } from "../utils";
|
||||
import * as Utils from "../utils";
|
||||
|
||||
export interface WindowsState {
|
||||
focused: string;
|
||||
|
|
@ -184,6 +185,17 @@ const windows = (
|
|||
},
|
||||
centerRequested: action.center
|
||||
};
|
||||
case RESET_WINDOW_LAYOUT:
|
||||
return {
|
||||
...state,
|
||||
genWindows: Utils.objectMap(state.genWindows, w => ({
|
||||
...w,
|
||||
// Not sure why TypeScript can't figure this out for itself.
|
||||
size: [0, 0] as [number, number]
|
||||
})),
|
||||
positions: {},
|
||||
centerRequested: true
|
||||
};
|
||||
case LOAD_SERIALIZED_STATE: {
|
||||
const {
|
||||
genWindows,
|
||||
|
|
@ -193,7 +205,7 @@ const windows = (
|
|||
} = action.serializedState.windows;
|
||||
return {
|
||||
...state,
|
||||
genWindows: objectMap(state.genWindows, (w, windowId) => {
|
||||
genWindows: Utils.objectMap(state.genWindows, (w, windowId) => {
|
||||
const serializedW = genWindows[windowId];
|
||||
if (serializedW == null) {
|
||||
return w;
|
||||
|
|
@ -201,7 +213,7 @@ const windows = (
|
|||
return { ...w, ...serializedW };
|
||||
}),
|
||||
// Note: We iterate genWindows here, since the positions object may be empty
|
||||
positions: objectMap(state.genWindows, (position, windowId) => {
|
||||
positions: Utils.objectMap(state.genWindows, (position, windowId) => {
|
||||
const serializedPosition = positions[windowId];
|
||||
if (serializedPosition == null) {
|
||||
return state.positions[windowId];
|
||||
|
|
@ -222,7 +234,7 @@ export function getSerializedState(
|
|||
state: WindowsState
|
||||
): WindowsSerializedStateV1 {
|
||||
return {
|
||||
genWindows: objectMap(state.genWindows, w => {
|
||||
genWindows: Utils.objectMap(state.genWindows, w => {
|
||||
return {
|
||||
size: w.size,
|
||||
open: w.open,
|
||||
|
|
|
|||
|
|
@ -417,13 +417,6 @@ export function getSerlializedState(state: AppState): SerializedStateV1 {
|
|||
};
|
||||
}
|
||||
|
||||
export function getVolume(state: AppState): number {
|
||||
return state.media.volume;
|
||||
}
|
||||
|
||||
export function getBalance(state: AppState): number {
|
||||
return state.media.balance;
|
||||
}
|
||||
export function getEqualizerEnabled(state: AppState): boolean {
|
||||
return state.equalizer.on;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -350,7 +350,8 @@ export type Action =
|
|||
| {
|
||||
type: "LOAD_SERIALIZED_STATE";
|
||||
serializedState: SerializedStateV1;
|
||||
};
|
||||
}
|
||||
| { type: "RESET_WINDOW_LAYOUT" };
|
||||
|
||||
export interface WebampWindow {
|
||||
title: string;
|
||||
|
|
|
|||
22
js/utils.ts
22
js/utils.ts
|
|
@ -342,3 +342,25 @@ export function findLastIndex<T>(arr: T[], cb: (val: T) => boolean) {
|
|||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
export function getWindowSize(): { width: number; height: number } {
|
||||
// Aparently this is crazy across browsers.
|
||||
return {
|
||||
width: Math.max(
|
||||
document.body.scrollWidth,
|
||||
document.documentElement.scrollWidth,
|
||||
document.body.offsetWidth,
|
||||
document.documentElement.offsetWidth,
|
||||
document.body.clientWidth,
|
||||
document.documentElement.clientWidth
|
||||
),
|
||||
height: Math.max(
|
||||
document.body.scrollHeight,
|
||||
document.documentElement.scrollHeight,
|
||||
document.body.offsetHeight,
|
||||
document.documentElement.offsetHeight,
|
||||
document.body.clientHeight,
|
||||
document.documentElement.clientHeight
|
||||
)
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@ import {
|
|||
seekForward,
|
||||
next,
|
||||
previous,
|
||||
updateWindowPositions
|
||||
updateWindowPositions,
|
||||
loadSerializedState,
|
||||
ensureWindowsAreOnScreen
|
||||
} from "./actionCreators";
|
||||
import { LOAD_STYLE } from "./constants";
|
||||
import { uniqueId, objectMap, objectForEach } from "./utils";
|
||||
|
|
@ -32,8 +34,7 @@ import {
|
|||
LOADED,
|
||||
REGISTER_VISUALIZER,
|
||||
SET_Z_INDEX,
|
||||
CLOSE_REQUESTED,
|
||||
LOAD_SERIALIZED_STATE
|
||||
CLOSE_REQUESTED
|
||||
} from "./actionTypes";
|
||||
import Emitter from "./emitter";
|
||||
|
||||
|
|
@ -128,6 +129,10 @@ class Winamp {
|
|||
this.store.dispatch({ type: NETWORK_DISCONNECTED })
|
||||
);
|
||||
|
||||
window.addEventListener("resize", () => {
|
||||
this.store.dispatch(ensureWindowsAreOnScreen());
|
||||
});
|
||||
|
||||
if (initialSkin) {
|
||||
this.store.dispatch(setSkinFromUrl(initialSkin.url));
|
||||
} else {
|
||||
|
|
@ -233,7 +238,7 @@ class Winamp {
|
|||
}
|
||||
|
||||
loadSerializedState(serializedState) {
|
||||
this.store.dispatch({ type: LOAD_SERIALIZED_STATE, serializedState });
|
||||
this.store.dispatch(loadSerializedState(serializedState));
|
||||
}
|
||||
|
||||
getSerializedState() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue