You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openNG/src/client/components/window-manager.jsx.bak

129 lines
4.1 KiB
React

'use strict';
const React = require("react");
const createReactClass = require("create-react-class");
const throttleit = require("throttleit");
const euclideanDistance = require("euclidean-distance");
const Window = require("./window");
module.exports = createReactClass({
displayName: "WindowManager",
getDefaultProps: function () {
return {
windowMoveThreshold: 6
};
},
getInitialState: function () {
return {
movingWindow: null,
activeWindow: null,
windowPositions: {}
};
},
lastMousePositionX: null,
lastMousePositionY: null,
componentDidMount: function () {
/* Due to how synthetic events work in React, we need to separate out the 'get coordinates from event' and 'do something with the coordinates' step; if we throttle the coordinate extraction logic, we'll run into problems when synthetic events have already been cleared for reuse by the time we try to obtain the coordinates. Therefore, `processMouseMove` contains all the throttled logic, whereas the coordinate extraction happens on *every* mousemove event. */
this.processMouseMove = throttleit(() => {
/* Yes, the below check is there for a reason; just in case the `movingWindow` state changes between the call to the throttled wrapper and the wrapped function itself. */
if (this.state.movingWindow != null) {
let thresholdMet = this.state.movingWindowThresholdMet;
/* Since we're outside of a React-controlled handler, we can't rely on React to batch updates. Therefore, we have to do our own impromptu batching. */
let stateToUpdate = {};
if (thresholdMet === false) {
let origin = [this.state.movingWindowOriginX, this.state.movingWindowOriginY];
let position = [this.lastMousePositionX, this.lastMousePositionY];
if (euclideanDistance(origin, position) > this.props.windowMoveThreshold) {
thresholdMet = true;
stateToUpdate.thresholdMet = true;
}
}
if (thresholdMet === true) {
/* TODO: Consider handling this move out-of-band, to avoid going through React's rendering cycle for every move event. */
stateToUpdate.windowPositions = Object.assign(this.state.windowPositions, {
[this.state.movingWindow]: {
x: this.lastMousePositionX - this.state.movingWindowBarX,
y: this.lastMousePositionY - this.state.movingWindowBarY
}
});
}
/* FIXME: Move this into the store? */
this.setState(stateToUpdate);
}
}, 10);
},
handleMouseMove: function(event) {
if (this.state.movingWindow != null) {
this.lastMousePositionX = event.pageX;
this.lastMousePositionY = event.pageY;
this.processMouseMove();
}
},
handleMouseUp: function () {
this.setState({
movingWindow: null
});
},
handleTitleMouseDown: function (window, event, barX, barY) {
this.setState({
movingWindow: window.id,
movingWindowThresholdMet: false,
movingWindowBarX: barX,
movingWindowBarY: barY,
movingWindowOriginX: event.pageX,
movingWindowOriginY: event.pageY
});
},
render: function () {
/* TODO: Tiled layout */
return (
<div className="windowManager" onMouseMove={this.handleMouseMove} onMouseUp={this.handleMouseUp}>
{this.props.children}
{this.props.store.getAll().toArray().map((window_) => {
let x, y;
let overrideWindowPosition = this.state.windowPositions[window_.id];
if (overrideWindowPosition != null) {
x = overrideWindowPosition.x;
y = overrideWindowPosition.y;
} else {
x = window_.initialX;
y = window_.initialY;
}
let windowStyle = {
transform: `translate(${x}px, ${y}px)`,
width: window_.width,
height: window_.height,
zIndex: window_.zIndex
};
let handlers = {
onTitleMouseDown: (...args) => {
return this.handleTitleMouseDown(window_, ...args);
},
onMouseDown: () => {
this.props.store.focus(window_.id);
},
onClose: () => {
this.props.store.close(window_.id);
}
};
return (<Window key={window_.id} style={windowStyle} title={window_.title} isActive={window_.isActive} {...handlers}>
{window_.contents}
</Window>);
})}
</div>
);
}
});