diff --git a/js/components/MilkdropWindow/PresetOverlay.js b/js/components/MilkdropWindow/PresetOverlay.js
new file mode 100644
index 00000000..87647017
--- /dev/null
+++ b/js/components/MilkdropWindow/PresetOverlay.js
@@ -0,0 +1,126 @@
+import React from "react";
+
+class PresetOverlay extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = { presetIdx: Math.max(props.currentPreset, 0) };
+ this._handleFocusedKeyboardInput = this._handleFocusedKeyboardInput.bind(
+ this
+ );
+ }
+ componentDidMount() {
+ this._unsubscribeFocusedKeyDown = this.props.onFocusedKeyDown(
+ this._handleFocusedKeyboardInput
+ );
+ }
+ componentWillUnmount() {
+ if (this._unsubscribeFocusedKeyDown) {
+ this._unsubscribeFocusedKeyDown();
+ }
+ }
+ _handleFocusedKeyboardInput(e) {
+ switch (e.keyCode) {
+ case 38: // up arrow
+ this.setState({ presetIdx: Math.max(this.state.presetIdx - 1, 0) });
+ e.stopPropagation();
+ break;
+ case 40: // down arrow
+ this.setState({
+ presetIdx: Math.min(
+ this.state.presetIdx + 1,
+ Object.keys(this.props.presets).length - 1
+ )
+ });
+ e.stopPropagation();
+ break;
+ case 13: // enter
+ this.props.selectPreset(this.state.presetIdx);
+ e.stopPropagation();
+ break;
+ case 27: // escape
+ this.props.closeOverlay();
+ e.stopPropagation();
+ break;
+ }
+ }
+ render() {
+ if (!this.props.presets) {
+ return (
+
+ Loading presets
+
+ );
+ }
+
+ // display highlighted preset in the middle if possible
+ const presetKeys = Object.keys(this.props.presets);
+ const numPresets = presetKeys.length;
+ let presetListLen = Math.floor(this.props.height / 20);
+ presetListLen = Math.min(Math.max(presetListLen, 3), numPresets);
+ presetListLen = presetListLen % 2 ? presetListLen : presetListLen - 1;
+ const halfPresetListLen = Math.floor(presetListLen / 2);
+ let startIdx = Math.max(this.state.presetIdx - halfPresetListLen, 0);
+ let endIdx = Math.min(startIdx + presetListLen, numPresets);
+ if (endIdx >= numPresets) {
+ startIdx = Math.max(endIdx - presetListLen, 0);
+ endIdx = Math.min(startIdx + presetListLen, numPresets);
+ }
+ const presets = presetKeys.slice(startIdx, endIdx);
+ const presetElms = presets.map((presetName, i) => {
+ let color;
+ if (i + startIdx === this.props.currentPreset) {
+ if (i + startIdx === this.state.presetIdx) {
+ color = "#FFCC22";
+ } else {
+ color = "#CCFF03";
+ }
+ } else if (i + startIdx === this.state.presetIdx) {
+ color = "#FF5050";
+ } else {
+ color = "#CCCCCC";
+ }
+ return (
+
+ {presetName}
+
+ );
+ });
+
+ return (
+
+ );
+ }
+}
+
+export default PresetOverlay;
diff --git a/js/components/MilkdropWindow/index.js b/js/components/MilkdropWindow/index.js
index 50d21bfe..f07a5d33 100644
--- a/js/components/MilkdropWindow/index.js
+++ b/js/components/MilkdropWindow/index.js
@@ -1,6 +1,7 @@
import React from "react";
import { connect } from "react-redux";
import screenfull from "screenfull";
+import PresetOverlay from "./PresetOverlay";
const USER_PRESET_TRANSITION_SECONDS = 5.7;
const PRESET_TRANSITION_SECONDS = 2.7;
@@ -9,10 +10,15 @@ const MILLISECONDS_BETWEEN_PRESET_TRANSITIONS = 15000;
class MilkdropWindow extends React.Component {
constructor(props) {
super(props);
- this._handleKeyboardInput = this._handleKeyboardInput.bind(this);
+ this.state = {
+ isFullscreen: false,
+ presetOverlay: false,
+ currentPreset: -1
+ };
this._handleFocusedKeyboardInput = this._handleFocusedKeyboardInput.bind(
this
);
+ this._handleFullscreenChange = this._handleFullscreenChange.bind(this);
}
componentDidMount() {
require.ensure(
@@ -29,11 +35,13 @@ class MilkdropWindow extends React.Component {
analyserNode.context,
this._canvasNode,
{
- width: this._canvasNode.width,
- height: this._canvasNode.height,
+ width: this.props.width,
+ height: this.props.height,
pixelRatio: window.devicePixelRatio || 1
}
);
+ this._canvasNode.width = this.props.width;
+ this._canvasNode.height = this.props.height;
this.visualizer.connectAudio(analyserNode);
this.visualizer.loadPreset(reactionDiffusion2, 0);
// Kick off the animation loop
@@ -44,6 +52,8 @@ class MilkdropWindow extends React.Component {
window.requestAnimationFrame(loop);
};
loop();
+
+ screenfull.onchange(this._handleFullscreenChange);
},
e => {
console.error("Error loading Butterchurn", e);
@@ -61,7 +71,6 @@ class MilkdropWindow extends React.Component {
this.presetRandomize = true;
this.presetCycle = true;
this._restartCycling();
- document.addEventListener("keydown", this._handleKeyboardInput);
this._unsubscribeFocusedKeyDown = this.props.onFocusedKeyDown(
this._handleFocusedKeyboardInput
);
@@ -75,10 +84,10 @@ class MilkdropWindow extends React.Component {
componentWillUnmount() {
this._pauseViz();
this._stopCycling();
- document.removeEventListener("keydown", this._handleKeyboardInput);
if (this._unsubscribeFocusedKeyDown) {
this._unsubscribeFocusedKeyDown();
}
+ screenfull.off("change", this._handleFullscreenChange);
}
componentDidUpdate(prevProps) {
if (
@@ -117,18 +126,24 @@ class MilkdropWindow extends React.Component {
this.visualizer.setRendererSize(width, height);
}
}
+ _handleFullscreenChange() {
+ if (screenfull.isFullscreen) {
+ this._setRendererSize(window.innerWidth, window.innerHeight);
+ } else {
+ this._setRendererSize(this.props.width, this.props.height);
+ }
+ this.setState({ isFullscreen: screenfull.isFullscreen });
+ }
_handleRequestFullsceen() {
if (screenfull.enabled) {
if (!screenfull.isFullscreen) {
- screenfull.request(this._canvasNode);
- this._setRendererSize(window.innerWidth, window.innerHeight);
+ screenfull.request(this._wrapperNode);
} else {
screenfull.exit();
- this._setRendererSize(this.props.width, this.props.height);
}
}
}
- _handleKeyboardInput(e) {
+ _handleFocusedKeyboardInput(e) {
switch (e.keyCode) {
case 32: // spacebar
this._nextPreset(USER_PRESET_TRANSITION_SECONDS);
@@ -142,6 +157,10 @@ class MilkdropWindow extends React.Component {
case 82: // R
this.presetRandomize = !this.presetRandomize;
break;
+ case 76: // L
+ this.setState({ presetOverlay: !this.state.presetOverlay });
+ e.stopPropagation();
+ break;
case 145: // scroll lock
case 125: // F14 (scroll lock for OS X)
this.presetCycle = !this.presetCycle;
@@ -149,9 +168,6 @@ class MilkdropWindow extends React.Component {
break;
}
}
- _handleFocusedKeyboardInput(e) {
- console.log("Milkdrop keyDown", e.keyCode);
- }
_nextPreset(blendTime) {
// The visualizer may not have initialized yet.
if (this.visualizer != null) {
@@ -166,26 +182,41 @@ class MilkdropWindow extends React.Component {
this.presetHistory.push(presetIdx);
this.visualizer.loadPreset(preset, blendTime);
this._restartCycling();
+ this.setState({ currentPreset: presetIdx });
}
}
_prevPreset(blendTime) {
if (this.presetHistory.length > 1 && this.visualizer != null) {
this.presetHistory.pop();
+ const prevPreset = this.presetHistory[this.presetHistory.length - 1];
this.visualizer.loadPreset(
- this.presets[
- this.presetKeys[this.presetHistory[this.presetHistory.length - 1]]
- ],
+ this.presets[this.presetKeys[prevPreset]],
blendTime
);
this._restartCycling();
+ this.setState({ currentPreset: prevPreset });
}
}
+ selectPreset(presetIdx) {
+ const preset = this.presets[this.presetKeys[presetIdx]];
+ this.presetHistory.push(presetIdx);
+ this.visualizer.loadPreset(preset, 0);
+ this._restartCycling();
+ this.setState({ currentPreset: presetIdx });
+ }
+ closePresetOverlay() {
+ this.setState({ presetOverlay: false });
+ }
render() {
+ const width = this.state.isFullscreen
+ ? window.innerWidth
+ : this.props.width;
+ const height = this.state.isFullscreen
+ ? window.innerHeight
+ : this.props.height;
return (
-