mirror of
https://github.com/captbaritone/webamp.git
synced 2026-01-23 10:15:31 +00:00
Maintain window relationships on resize
This commit is contained in:
parent
6200b87295
commit
6cd8e7112b
9 changed files with 169 additions and 63 deletions
|
|
@ -16,7 +16,10 @@ import {
|
|||
getPlaylistURL,
|
||||
getSelectedTrackObjects,
|
||||
getTracks,
|
||||
getTrackIsVisibleFunction
|
||||
getTrackIsVisibleFunction,
|
||||
getWindowGraph,
|
||||
getWindowPositions,
|
||||
getWindowSizes
|
||||
} from "./selectors";
|
||||
|
||||
import {
|
||||
|
|
@ -59,10 +62,14 @@ import {
|
|||
TOGGLE_PLAYLIST_SHADE_MODE,
|
||||
MEDIA_TAG_REQUEST_INITIALIZED,
|
||||
MEDIA_TAG_REQUEST_FAILED,
|
||||
PLAYLIST_SIZE_CHANGED
|
||||
PLAYLIST_SIZE_CHANGED,
|
||||
UPDATE_WINDOW_POSITIONS,
|
||||
TOGGLE_DOUBLESIZE_MODE
|
||||
} from "./actionTypes";
|
||||
|
||||
import LoadQueue from "./loadQueue";
|
||||
import { getPositionDiff } from "./resizeUtils";
|
||||
import { applyDiff } from "./snapUtils";
|
||||
|
||||
// Lower is better
|
||||
const DURATION_VISIBLE_PRIORITY = 5;
|
||||
|
|
@ -469,16 +476,57 @@ export function downloadPreset() {
|
|||
};
|
||||
}
|
||||
|
||||
// Dispatch an action and, if needed rearrange the windows to preserve
|
||||
// the existing edge relationship.
|
||||
//
|
||||
// Works by checking the edges before the action is dispatched. Then,
|
||||
// after disatching, calculating what position change would be required
|
||||
// to restore those relationships.
|
||||
function withWindowGraphIntegrity(action) {
|
||||
return (dispatch, getState) => {
|
||||
const state = getState();
|
||||
const graph = getWindowGraph(state);
|
||||
const originalSizes = getWindowSizes(state);
|
||||
|
||||
dispatch(action);
|
||||
|
||||
const newSizes = getWindowSizes(getState());
|
||||
const sizeDiff = {};
|
||||
for (const window of Object.keys(newSizes)) {
|
||||
const original = originalSizes[window];
|
||||
const current = newSizes[window];
|
||||
sizeDiff[window] = {
|
||||
height: current.height - original.height,
|
||||
width: current.width - original.width
|
||||
};
|
||||
}
|
||||
|
||||
const positionDiff = getPositionDiff(graph, sizeDiff);
|
||||
const windowPositions = getWindowPositions(state);
|
||||
|
||||
const newPositions = {};
|
||||
for (const key of Object.keys(windowPositions)) {
|
||||
newPositions[key] = applyDiff(windowPositions[key], positionDiff[key]);
|
||||
}
|
||||
|
||||
dispatch(updateWindowPositions(newPositions));
|
||||
};
|
||||
}
|
||||
|
||||
export function toggleDoubleSizeMode() {
|
||||
return withWindowGraphIntegrity({ type: TOGGLE_DOUBLESIZE_MODE });
|
||||
}
|
||||
|
||||
export function toggleEqualizerShadeMode() {
|
||||
return { type: TOGGLE_EQUALIZER_SHADE_MODE };
|
||||
return withWindowGraphIntegrity({ type: TOGGLE_EQUALIZER_SHADE_MODE });
|
||||
}
|
||||
|
||||
export function toggleMainWindowShadeMode() {
|
||||
return { type: TOGGLE_MAIN_SHADE_MODE };
|
||||
return withWindowGraphIntegrity({ type: TOGGLE_MAIN_SHADE_MODE });
|
||||
}
|
||||
|
||||
export function togglePlaylistShadeMode() {
|
||||
return { type: TOGGLE_PLAYLIST_SHADE_MODE };
|
||||
return withWindowGraphIntegrity({ type: TOGGLE_PLAYLIST_SHADE_MODE });
|
||||
}
|
||||
|
||||
export function closeEqualizerWindow() {
|
||||
|
|
@ -625,3 +673,7 @@ export function downloadHtmlPlaylist() {
|
|||
downloadURI(uri, "Winamp Playlist.html");
|
||||
};
|
||||
}
|
||||
|
||||
export function updateWindowPositions(positions) {
|
||||
return { type: UPDATE_WINDOW_POSITIONS, positions };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,3 +64,4 @@ export const MEDIA_TAG_REQUEST_INITIALIZED = "MEDIA_TAG_REQUEST_INITIALIZED";
|
|||
export const MEDIA_TAG_REQUEST_FAILED = "MEDIA_TAG_REQUEST_FAILED";
|
||||
export const NETWORK_CONNECTED = "NETWORK_CONNECTED";
|
||||
export const NETWORK_DISCONNECTED = "NETWORK_DISCONNECTED";
|
||||
export const UPDATE_WINDOW_POSITIONS = "UPDATE_WINDOW_POSITIONS";
|
||||
|
|
|
|||
|
|
@ -2,11 +2,8 @@ import React from "react";
|
|||
import { connect } from "react-redux";
|
||||
import classnames from "classnames";
|
||||
|
||||
import {
|
||||
SET_FOCUS,
|
||||
TOGGLE_DOUBLESIZE_MODE,
|
||||
UNSET_FOCUS
|
||||
} from "../../actionTypes";
|
||||
import { SET_FOCUS, UNSET_FOCUS } from "../../actionTypes";
|
||||
import { toggleDoubleSizeMode } from "../../actionCreators";
|
||||
|
||||
const ClutterBar = props => (
|
||||
<div id="clutter-bar">
|
||||
|
|
@ -31,7 +28,7 @@ const mapStateToProps = state => ({
|
|||
const mapDispatchToProps = dispatch => ({
|
||||
handleMouseDown: () => dispatch({ type: SET_FOCUS, input: "double" }),
|
||||
handleMouseUp: () => {
|
||||
dispatch({ type: TOGGLE_DOUBLESIZE_MODE });
|
||||
dispatch(toggleDoubleSizeMode());
|
||||
dispatch({ type: UNSET_FOCUS });
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,11 +3,7 @@ import { connect } from "react-redux";
|
|||
import classnames from "classnames";
|
||||
import { getOrderedTracks, getMinimalMediaText } from "../../selectors";
|
||||
import { getTimeStr } from "../../utils";
|
||||
import {
|
||||
TOGGLE_PLAYLIST_WINDOW,
|
||||
TOGGLE_PLAYLIST_SHADE_MODE,
|
||||
SET_FOCUSED_WINDOW
|
||||
} from "../../actionTypes";
|
||||
import { TOGGLE_PLAYLIST_WINDOW, SET_FOCUSED_WINDOW } from "../../actionTypes";
|
||||
|
||||
import {
|
||||
WINDOWS,
|
||||
|
|
@ -16,6 +12,7 @@ import {
|
|||
CHARACTER_WIDTH,
|
||||
UTF8_ELLIPSIS
|
||||
} from "../../constants";
|
||||
import { togglePlaylistShadeMode } from "../../actionCreators";
|
||||
import CharacterString from "../CharacterString";
|
||||
import PlaylistResizeTarget from "./PlaylistResizeTarget";
|
||||
|
||||
|
|
@ -83,7 +80,7 @@ const mapDispatchToProps = dispatch => ({
|
|||
focusPlaylist: () =>
|
||||
dispatch({ type: SET_FOCUSED_WINDOW, window: WINDOWS.PLAYLIST }),
|
||||
close: () => dispatch({ type: TOGGLE_PLAYLIST_WINDOW }),
|
||||
toggleShade: () => dispatch({ type: TOGGLE_PLAYLIST_SHADE_MODE })
|
||||
toggleShade: () => dispatch(togglePlaylistShadeMode())
|
||||
});
|
||||
|
||||
const mapStateToProps = state => {
|
||||
|
|
|
|||
|
|
@ -2,18 +2,8 @@ import React from "react";
|
|||
import { connect } from "react-redux";
|
||||
import classnames from "classnames";
|
||||
|
||||
import {
|
||||
WINDOWS,
|
||||
PLAYLIST_RESIZE_SEGMENT_WIDTH,
|
||||
PLAYLIST_RESIZE_SEGMENT_HEIGHT,
|
||||
MIN_PLAYLIST_WINDOW_WIDTH,
|
||||
TRACK_HEIGHT
|
||||
} from "../../constants";
|
||||
import {
|
||||
TOGGLE_PLAYLIST_WINDOW,
|
||||
TOGGLE_PLAYLIST_SHADE_MODE,
|
||||
SET_FOCUSED_WINDOW
|
||||
} from "../../actionTypes";
|
||||
import { WINDOWS, TRACK_HEIGHT } from "../../constants";
|
||||
import { TOGGLE_PLAYLIST_WINDOW, SET_FOCUSED_WINDOW } from "../../actionTypes";
|
||||
import {
|
||||
toggleVisualizerStyle,
|
||||
scrollUpFourTracks,
|
||||
|
|
@ -22,7 +12,7 @@ import {
|
|||
togglePlaylistShadeMode,
|
||||
scrollVolume
|
||||
} from "../../actionCreators";
|
||||
import { getScrollOffset } from "../../selectors";
|
||||
import { getScrollOffset, getPlaylistWindowPixelSize } from "../../selectors";
|
||||
|
||||
import { clamp } from "../../utils";
|
||||
import DropTarget from "../DropTarget";
|
||||
|
|
@ -39,8 +29,6 @@ import ScrollBar from "./ScrollBar";
|
|||
|
||||
import "../../../css/playlist-window.css";
|
||||
|
||||
const MIN_WINDOW_HEIGHT = 116;
|
||||
|
||||
class PlaylistWindow extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
|
@ -63,6 +51,7 @@ class PlaylistWindow extends React.Component {
|
|||
focusPlaylist,
|
||||
focused,
|
||||
playlistSize,
|
||||
playlistWindowPixelSize,
|
||||
playlistShade,
|
||||
close,
|
||||
toggleShade
|
||||
|
|
@ -75,10 +64,8 @@ class PlaylistWindow extends React.Component {
|
|||
color: skinPlaylistStyle.normal,
|
||||
backgroundColor: skinPlaylistStyle.normalbg,
|
||||
fontFamily: `${skinPlaylistStyle.font}, Arial, sans-serif`,
|
||||
height: `${MIN_WINDOW_HEIGHT +
|
||||
playlistSize[1] * PLAYLIST_RESIZE_SEGMENT_HEIGHT}px`,
|
||||
width: `${MIN_PLAYLIST_WINDOW_WIDTH +
|
||||
playlistSize[0] * PLAYLIST_RESIZE_SEGMENT_WIDTH}px`
|
||||
height: `${playlistWindowPixelSize.height}px`,
|
||||
width: `${playlistWindowPixelSize.width}px`
|
||||
};
|
||||
|
||||
const classes = classnames("window", "draggable", {
|
||||
|
|
@ -162,7 +149,7 @@ const mapDispatchToProps = {
|
|||
window: WINDOWS.PLAYLIST
|
||||
}),
|
||||
close: () => ({ type: TOGGLE_PLAYLIST_WINDOW }),
|
||||
toggleShade: () => ({ type: TOGGLE_PLAYLIST_SHADE_MODE }),
|
||||
toggleShade: togglePlaylistShadeMode,
|
||||
toggleVisualizerStyle,
|
||||
scrollUpFourTracks,
|
||||
scrollDownFourTracks,
|
||||
|
|
@ -183,6 +170,7 @@ const mapStateToProps = state => {
|
|||
return {
|
||||
offset: getScrollOffset(state),
|
||||
maxTrackIndex: trackOrder.length - 1,
|
||||
playlistWindowPixelSize: getPlaylistWindowPixelSize(state),
|
||||
focused,
|
||||
skinPlaylistStyle,
|
||||
playlistSize,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { connect } from "react-redux";
|
||||
|
||||
import {
|
||||
snapDiffManyToMany,
|
||||
|
|
@ -10,6 +11,8 @@ import {
|
|||
applyDiff,
|
||||
applyMultipleDiffs
|
||||
} from "../snapUtils";
|
||||
import { getWindowPositions } from "../selectors";
|
||||
import { updateWindowPositions } from "../actionCreators";
|
||||
|
||||
const WINDOW_HEIGHT = 116;
|
||||
const WINDOW_WIDTH = 275;
|
||||
|
|
@ -25,7 +28,6 @@ class WindowManager extends React.Component {
|
|||
constructor(props) {
|
||||
super(props);
|
||||
this.windowNodes = {};
|
||||
this.state = {};
|
||||
this.getRef = this.getRef.bind(this);
|
||||
this.handleMouseDown = this.handleMouseDown.bind(this);
|
||||
this.centerWindows = this.centerWindows.bind(this);
|
||||
|
|
@ -62,17 +64,17 @@ class WindowManager extends React.Component {
|
|||
width = container.scrollWidth;
|
||||
height = container.scrollHeight;
|
||||
}
|
||||
const state = {};
|
||||
const windowPositions = {};
|
||||
const keys = this.windowKeys();
|
||||
const totalHeight = keys.length * WINDOW_HEIGHT;
|
||||
keys.forEach((key, i) => {
|
||||
const offset = WINDOW_HEIGHT * i;
|
||||
state[key] = {
|
||||
left: offsetLeft + (width / 2 - WINDOW_WIDTH / 2),
|
||||
top: offsetTop + (height / 2 - totalHeight / 2 + offset)
|
||||
windowPositions[key] = {
|
||||
x: offsetLeft + (width / 2 - WINDOW_WIDTH / 2),
|
||||
y: offsetTop + (height / 2 - totalHeight / 2 + offset)
|
||||
};
|
||||
});
|
||||
this.setState(state);
|
||||
this.props.updateWindowPositions(windowPositions);
|
||||
}
|
||||
|
||||
getRef(key, node) {
|
||||
|
|
@ -154,16 +156,12 @@ class WindowManager extends React.Component {
|
|||
|
||||
const finalDiff = applyMultipleDiffs(proposedDiff, snapDiff, withinDiff);
|
||||
|
||||
const stateDiff = moving.reduce((diff, window) => {
|
||||
const newWindowLocation = applyDiff(window, finalDiff);
|
||||
diff[window.key] = {
|
||||
top: newWindowLocation.y,
|
||||
left: newWindowLocation.x
|
||||
};
|
||||
const windowPositionDiff = moving.reduce((diff, window) => {
|
||||
diff[window.key] = applyDiff(window, finalDiff);
|
||||
return diff;
|
||||
}, {});
|
||||
|
||||
this.setState(stateDiff);
|
||||
this.props.updateWindowPositions(windowPositionDiff);
|
||||
};
|
||||
|
||||
const removeListeners = () => {
|
||||
|
|
@ -198,13 +196,13 @@ class WindowManager extends React.Component {
|
|||
return (
|
||||
<div style={parentStyle}>
|
||||
{this.windowKeys().map(key => {
|
||||
const position = this.state[key];
|
||||
const position = this.props.windowPositions[key];
|
||||
return (
|
||||
position && (
|
||||
<div
|
||||
onMouseDown={e => this.handleMouseDown(key, e)}
|
||||
ref={node => this.getRef(key, node)}
|
||||
style={{ ...style, ...position }}
|
||||
style={{ ...style, left: position.x, top: position.y }}
|
||||
key={key}
|
||||
>
|
||||
{this.props.windows[key]}
|
||||
|
|
@ -222,4 +220,12 @@ WindowManager.propTypes = {
|
|||
container: PropTypes.instanceOf(Element)
|
||||
};
|
||||
|
||||
export default WindowManager;
|
||||
const mapStateToProps = state => ({
|
||||
windowPositions: getWindowPositions(state)
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
updateWindowPositions
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(WindowManager);
|
||||
|
|
|
|||
|
|
@ -11,14 +11,11 @@ import {
|
|||
reverseList,
|
||||
nextN,
|
||||
next,
|
||||
previous
|
||||
previous,
|
||||
toggleDoubleSizeMode
|
||||
} from "./actionCreators";
|
||||
|
||||
import {
|
||||
TOGGLE_DOUBLESIZE_MODE,
|
||||
TOGGLE_TIME_MODE,
|
||||
TOGGLE_LLAMA_MODE
|
||||
} from "./actionTypes";
|
||||
import { TOGGLE_TIME_MODE, TOGGLE_LLAMA_MODE } from "./actionTypes";
|
||||
|
||||
import { arraysAreEqual } from "./utils";
|
||||
|
||||
|
|
@ -39,7 +36,7 @@ export default function(dispatch) {
|
|||
// Is CTRL depressed?
|
||||
switch (e.keyCode) {
|
||||
case 68: // CTRL+D
|
||||
dispatch({ type: TOGGLE_DOUBLESIZE_MODE });
|
||||
dispatch(toggleDoubleSizeMode());
|
||||
e.preventDefault(); // Supress the "Bookmark" action on windows.
|
||||
break;
|
||||
case 76: // CTRL+L FIXME
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@ import {
|
|||
CLOSE_EQUALIZER_WINDOW,
|
||||
TOGGLE_PLAYLIST_WINDOW,
|
||||
CLOSE_GEN_WINDOW,
|
||||
OPEN_GEN_WINDOW
|
||||
OPEN_GEN_WINDOW,
|
||||
UPDATE_WINDOW_POSITIONS
|
||||
} from "../actionTypes";
|
||||
|
||||
import { arrayWith, arrayWithout } from "../utils";
|
||||
|
|
@ -15,7 +16,8 @@ const defaultWindowsState = {
|
|||
equalizer: true,
|
||||
playlist: true,
|
||||
// openGenWindows: ["AVS_WINDOW"]
|
||||
openGenWindows: []
|
||||
openGenWindows: [],
|
||||
positions: {}
|
||||
};
|
||||
|
||||
const windows = (state = defaultWindowsState, action) => {
|
||||
|
|
@ -38,6 +40,11 @@ const windows = (state = defaultWindowsState, action) => {
|
|||
...state,
|
||||
openGenWindows: arrayWith(state.openGenWindow, action.windowId)
|
||||
};
|
||||
case UPDATE_WINDOW_POSITIONS:
|
||||
return {
|
||||
...state,
|
||||
positions: { ...state.positions, ...action.positions }
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,14 @@ import { createSelector } from "reselect";
|
|||
import { denormalize, getTimeStr, clamp, percentToIndex } from "./utils";
|
||||
import {
|
||||
BANDS,
|
||||
TRACK_HEIGHT,
|
||||
PLAYLIST_RESIZE_SEGMENT_WIDTH,
|
||||
PLAYLIST_RESIZE_SEGMENT_HEIGHT,
|
||||
TRACK_HEIGHT
|
||||
MIN_PLAYLIST_WINDOW_WIDTH
|
||||
} from "./constants";
|
||||
import { createPlaylistURL } from "./playlistHtml";
|
||||
import * as fromPlaylist from "./reducers/playlist";
|
||||
import { generateGraph } from "./resizeUtils";
|
||||
|
||||
export const getEqfData = state => {
|
||||
const { sliders } = state.equalizer;
|
||||
|
|
@ -228,3 +231,61 @@ export const getPlaylistURL = createSelector(
|
|||
)
|
||||
})
|
||||
);
|
||||
|
||||
export function getWindowPositions(state) {
|
||||
return state.windows.positions;
|
||||
}
|
||||
|
||||
const WINDOW_WIDTH = 275;
|
||||
const WINDOW_HEIGHT = 116;
|
||||
const DEFAUT_WINDOW_SIZE = {
|
||||
height: WINDOW_HEIGHT,
|
||||
width: WINDOW_WIDTH
|
||||
};
|
||||
const SHADE_WINDOW_HEIGHT = 14;
|
||||
|
||||
export function getPlaylistWindowPixelSize(state) {
|
||||
const { playlistSize } = state.display;
|
||||
return {
|
||||
height: WINDOW_HEIGHT + playlistSize[1] * PLAYLIST_RESIZE_SEGMENT_HEIGHT,
|
||||
width:
|
||||
MIN_PLAYLIST_WINDOW_WIDTH +
|
||||
playlistSize[0] * PLAYLIST_RESIZE_SEGMENT_WIDTH
|
||||
};
|
||||
}
|
||||
|
||||
function getGenericWindowSize(size, shade, doubled) {
|
||||
const doubledMultiplier = doubled ? 2 : 1;
|
||||
return {
|
||||
height: (shade ? SHADE_WINDOW_HEIGHT : size.height) * doubledMultiplier,
|
||||
width: size.width * doubledMultiplier
|
||||
};
|
||||
}
|
||||
|
||||
export function getWindowSizes(state) {
|
||||
const { doubled, mainShade, equalizerShade, playlistShade } = state.display;
|
||||
const main = getGenericWindowSize(DEFAUT_WINDOW_SIZE, mainShade, doubled);
|
||||
const equalizer = getGenericWindowSize(
|
||||
DEFAUT_WINDOW_SIZE,
|
||||
equalizerShade,
|
||||
doubled
|
||||
);
|
||||
const playlist = getGenericWindowSize(
|
||||
getPlaylistWindowPixelSize(state),
|
||||
playlistShade,
|
||||
false // The playlist cannot be doubled
|
||||
);
|
||||
return { main, equalizer, playlist };
|
||||
}
|
||||
|
||||
export const getWindowGraph = createSelector(
|
||||
getWindowPositions,
|
||||
getWindowSizes,
|
||||
(windowPositions, windowSizes) => {
|
||||
const windowData = [];
|
||||
for (const key of Object.keys(windowPositions)) {
|
||||
windowData.push({ key, ...windowPositions[key], ...windowSizes[key] });
|
||||
}
|
||||
return generateGraph(windowData);
|
||||
}
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue