Moved undo and redo into tool settings and implemented it for fog

This commit is contained in:
Mitchell McCaffrey 2020-04-30 09:29:16 +10:00
parent ccaa51fe84
commit 88b4785307
9 changed files with 202 additions and 102 deletions

View File

@ -26,9 +26,11 @@ function Map({
onMapChange,
onMapStateChange,
onMapDraw,
onMapDrawUndo,
onMapDrawRedo,
onFogDraw,
onMapUndo,
onMapRedo,
onFogDrawUndo,
onFogDrawRedo,
allowMapDrawing,
allowFogDrawing,
allowTokenChange,
@ -82,28 +84,40 @@ function Map({
timestamp: Date.now(),
});
}
if (action === "mapUndo") {
onMapDrawUndo();
}
if (action === "mapRedo") {
onMapDrawRedo();
}
if (action === "fogUndo") {
onFogDrawUndo();
}
if (action === "fogRedo") {
onFogDrawRedo();
}
}
const [mapShapes, setMapShapes] = useState([]);
function handleMapShapeAdd(shape) {
onMapDraw({ type: "add", shapes: [shape], timestamp: Date.now() });
onMapDraw({ type: "add", shapes: [shape] });
}
function handleMapShapeRemove(shapeId) {
onMapDraw({ type: "remove", shapeIds: [shapeId], timestamp: Date.now() });
onMapDraw({ type: "remove", shapeIds: [shapeId] });
}
const [fogShapes, setFogShapes] = useState([]);
function handleFogShapeAdd(shape) {
onFogDraw({ type: "add", shapes: [shape], timestamp: Date.now() });
onFogDraw({ type: "add", shapes: [shape] });
}
function handleFogShapeRemove(shapeId) {
onFogDraw({ type: "remove", shapeIds: [shapeId], timestamp: Date.now() });
onFogDraw({ type: "remove", shapeIds: [shapeId] });
}
function handleFogShapeEdit(shape) {
onFogDraw({ type: "edit", shapes: [shape], timestamp: Date.now() });
onFogDraw({ type: "edit", shapes: [shape] });
}
// Replay the draw actions and convert them to shapes for the map drawing
@ -141,28 +155,38 @@ function Map({
disabledControls.push("shape");
disabledControls.push("erase");
}
// If no actions that can be undone
if (!allowFogDrawing && !allowMapDrawing) {
disabledControls.push("undo");
disabledControls.push("redo");
}
if (!map) {
disabledControls.push("pan");
}
if (mapShapes.length === 0) {
disabledControls.push("erase");
}
if (!mapState || mapState.mapDrawActionIndex < 0) {
disabledControls.push("undo");
}
if (!allowFogDrawing) {
disabledControls.push("fog");
}
const disabledSettings = { fog: [], brush: [], shape: [], erase: [] };
if (!mapState || mapState.mapDrawActionIndex < 0) {
disabledSettings.brush.push("undo");
disabledSettings.shape.push("undo");
disabledSettings.erase.push("undo");
}
if (
!mapState ||
mapState.mapDrawActionIndex === mapState.mapDrawActions.length - 1
) {
disabledControls.push("redo");
disabledSettings.brush.push("redo");
disabledSettings.shape.push("redo");
disabledSettings.erase.push("redo");
}
if (fogShapes.length === 0) {
disabledSettings.fog.push("undo");
}
if (
!mapState ||
mapState.fogDrawActionIndex === mapState.fogDrawActions.length - 1
) {
disabledSettings.fog.push("redo");
}
/**
@ -262,8 +286,7 @@ function Map({
onToolSettingChange={handleToolSettingChange}
onToolAction={handleToolAction}
disabledControls={disabledControls}
onUndo={onMapUndo}
onRedo={onMapRedo}
disabledSettings={disabledSettings}
/>
);
return (

View File

@ -16,8 +16,6 @@ import FogToolIcon from "../../icons/FogToolIcon";
import BrushToolIcon from "../../icons/BrushToolIcon";
import ShapeToolIcon from "../../icons/ShapeToolIcon";
import EraseToolIcon from "../../icons/EraseToolIcon";
import UndoIcon from "../../icons/UndoIcon";
import RedoIcon from "../../icons/RedoIcon";
import ExpandMoreIcon from "../../icons/ExpandMoreIcon";
function MapContols({
@ -30,8 +28,7 @@ function MapContols({
onToolSettingChange,
onToolAction,
disabledControls,
onUndo,
onRedo,
disabledSettings,
}) {
const [isExpanded, setIsExpanded] = useState(false);
@ -93,25 +90,6 @@ function MapContols({
</RadioIconButton>
)),
},
{
id: "history",
component: (
<>
<IconButton
onClick={onUndo}
disabled={disabledControls.includes("undo")}
>
<UndoIcon />
</IconButton>
<IconButton
onClick={onRedo}
disabled={disabledControls.includes("redo")}
>
<RedoIcon />
</IconButton>
</>
),
},
];
let controls = null;
@ -191,6 +169,7 @@ function MapContols({
onToolSettingChange(selectedToolId, change)
}
onToolAction={onToolAction}
disabledActions={disabledSettings[selectedToolId]}
/>
</Box>
);
@ -199,13 +178,6 @@ function MapContols({
}
}
// Move back to pan tool if selected tool becomes disabled
useEffect(() => {
if (disabledControls.includes(selectedToolId)) {
onSelectedToolChange("pan");
}
}, [disabledControls]);
// Stop map drawing from happening when selecting controls
// Not using react events as they seem to trigger after dom events
useEffect(() => {

View File

@ -8,9 +8,17 @@ import RadioIconButton from "./RadioIconButton";
import BrushStrokeIcon from "../../../icons/BrushStrokeIcon";
import BrushFillIcon from "../../../icons/BrushFillIcon";
import UndoButton from "./UndoButton";
import RedoButton from "./RedoButton";
import Divider from "./Divider";
function BrushToolSettings({ settings, onSettingChange }) {
function BrushToolSettings({
settings,
onSettingChange,
onToolAction,
disabledActions,
}) {
return (
<Flex sx={{ alignItems: "center" }}>
<ColorControl
@ -37,6 +45,15 @@ function BrushToolSettings({ settings, onSettingChange }) {
useBlending={settings.useBlending}
onBlendingChange={(useBlending) => onSettingChange({ useBlending })}
/>
<Divider vertical />
<UndoButton
onClick={() => onToolAction("mapUndo")}
disabled={disabledActions.includes("undo")}
/>
<RedoButton
onClick={() => onToolAction("mapRedo")}
disabled={disabledActions.includes("redo")}
/>
</Flex>
);
}

View File

@ -3,7 +3,12 @@ import { Flex, IconButton } from "theme-ui";
import EraseAllIcon from "../../../icons/EraseAllIcon";
function EraseToolSettings({ onToolAction }) {
import UndoButton from "./UndoButton";
import RedoButton from "./RedoButton";
import Divider from "./Divider";
function EraseToolSettings({ onToolAction, disabledActions }) {
return (
<Flex sx={{ alignItems: "center" }}>
<IconButton
@ -13,6 +18,15 @@ function EraseToolSettings({ onToolAction }) {
>
<EraseAllIcon />
</IconButton>
<Divider vertical />
<UndoButton
onClick={() => onToolAction("mapUndo")}
disabled={disabledActions.includes("undo")}
/>
<RedoButton
onClick={() => onToolAction("mapRedo")}
disabled={disabledActions.includes("redo")}
/>
</Flex>
);
}

View File

@ -9,9 +9,17 @@ import FogAddIcon from "../../../icons/FogAddIcon";
import FogRemoveIcon from "../../../icons/FogRemoveIcon";
import FogToggleIcon from "../../../icons/FogToggleIcon";
import UndoButton from "./UndoButton";
import RedoButton from "./RedoButton";
import Divider from "./Divider";
function BrushToolSettings({ settings, onSettingChange }) {
function BrushToolSettings({
settings,
onSettingChange,
onToolAction,
disabledActions,
}) {
return (
<Flex sx={{ alignItems: "center" }}>
<RadioIconButton
@ -48,6 +56,15 @@ function BrushToolSettings({ settings, onSettingChange }) {
onSettingChange({ useGridSnapping })
}
/>
<Divider vertical />
<UndoButton
onClick={() => onToolAction("fogUndo")}
disabled={disabledActions.includes("undo")}
/>
<RedoButton
onClick={() => onToolAction("fogRedo")}
disabled={disabledActions.includes("redo")}
/>
</Flex>
);
}

View File

@ -0,0 +1,14 @@
import React from "react";
import { IconButton } from "theme-ui";
import RedoIcon from "../../../icons/RedoIcon";
function RedoButton({ onClick, disabled }) {
return (
<IconButton onClick={onClick} disabled={disabled}>
<RedoIcon />
</IconButton>
);
}
export default RedoButton;

View File

@ -9,9 +9,17 @@ import ShapeRectangleIcon from "../../../icons/ShapeRectangleIcon";
import ShapeCircleIcon from "../../../icons/ShapeCircleIcon";
import ShapeTriangleIcon from "../../../icons/ShapeTriangleIcon";
import UndoButton from "./UndoButton";
import RedoButton from "./RedoButton";
import Divider from "./Divider";
function ShapeToolSettings({ settings, onSettingChange }) {
function ShapeToolSettings({
settings,
onSettingChange,
onToolAction,
disabledActions,
}) {
return (
<Flex sx={{ alignItems: "center" }}>
<ColorControl
@ -45,6 +53,15 @@ function ShapeToolSettings({ settings, onSettingChange }) {
useBlending={settings.useBlending}
onBlendingChange={(useBlending) => onSettingChange({ useBlending })}
/>
<Divider vertical />
<UndoButton
onClick={() => onToolAction("mapUndo")}
disabled={disabledActions.includes("undo")}
/>
<RedoButton
onClick={() => onToolAction("mapRedo")}
disabled={disabledActions.includes("redo")}
/>
</Flex>
);
}

View File

@ -0,0 +1,14 @@
import React from "react";
import { IconButton } from "theme-ui";
import UndoIcon from "../../../icons/UndoIcon";
function UndoButton({ onClick, disabled }) {
return (
<IconButton onClick={onClick} disabled={disabled}>
<UndoIcon />
</IconButton>
);
}
export default UndoButton;

View File

@ -118,82 +118,92 @@ function Game() {
}
}
function addNewMapDrawActions(actions) {
function addMapDrawActions(actions, indexKey, actionsKey) {
setMapState((prevMapState) => {
const newActions = [
...prevMapState.mapDrawActions.slice(
0,
prevMapState.mapDrawActionIndex + 1
),
...prevMapState[actionsKey].slice(0, prevMapState[indexKey] + 1),
...actions,
];
const newIndex = newActions.length - 1;
return {
...prevMapState,
mapDrawActions: newActions,
mapDrawActionIndex: newIndex,
[actionsKey]: newActions,
[indexKey]: newIndex,
};
});
}
function updateDrawActionIndex(change, indexKey, actionsKey, peerId) {
const newIndex = Math.min(
Math.max(mapState[indexKey] + change, -1),
mapState[actionsKey].length - 1
);
setMapState((prevMapState) => ({
...prevMapState,
[indexKey]: newIndex,
}));
return newIndex;
}
function handleMapDraw(action) {
addNewMapDrawActions([action]);
addMapDrawActions([action], "mapDrawActionIndex", "mapDrawActions");
for (let peer of Object.values(peers)) {
peer.connection.send({ id: "mapDraw", data: [action] });
}
}
function handleMapUndo() {
// TODO: Check whether to pull from draw actions or fog actions
const newIndex = Math.max(mapState.mapDrawActionIndex - 1, -1);
setMapState((prevMapState) => ({
...prevMapState,
mapDrawActionIndex: newIndex,
}));
for (let peer of Object.values(peers)) {
peer.connection.send({ id: "mapDrawIndex", data: newIndex });
}
}
function handleMapRedo() {
const newIndex = Math.min(
mapState.mapDrawActionIndex + 1,
mapState.mapDrawActions.length - 1
function handleMapDrawUndo() {
const index = updateDrawActionIndex(
-1,
"mapDrawActionIndex",
"mapDrawActions"
);
setMapState((prevMapState) => ({
...prevMapState,
mapDrawActionIndex: newIndex,
}));
for (let peer of Object.values(peers)) {
peer.connection.send({ id: "mapDrawIndex", data: newIndex });
peer.connection.send({ id: "mapDrawIndex", data: index });
}
}
function addNewFogDrawActions(actions) {
setMapState((prevMapState) => {
const newActions = [
...prevMapState.fogDrawActions.slice(
0,
prevMapState.fogDrawActionIndex + 1
),
...actions,
];
const newIndex = newActions.length - 1;
return {
...prevMapState,
fogDrawActions: newActions,
fogDrawActionIndex: newIndex,
};
});
function handleMapDrawRedo() {
const index = updateDrawActionIndex(
1,
"mapDrawActionIndex",
"mapDrawActions"
);
for (let peer of Object.values(peers)) {
peer.connection.send({ id: "mapDrawIndex", data: index });
}
}
function handleFogDraw(action) {
addNewFogDrawActions([action]);
addMapDrawActions([action], "fogDrawActionIndex", "fogDrawActions");
for (let peer of Object.values(peers)) {
peer.connection.send({ id: "mapFog", data: [action] });
}
}
function handleFogDrawUndo() {
const index = updateDrawActionIndex(
-1,
"fogDrawActionIndex",
"fogDrawActions"
);
for (let peer of Object.values(peers)) {
peer.connection.send({ id: "fogDrawIndex", data: index });
}
}
function handleFogDrawRedo() {
const index = updateDrawActionIndex(
1,
"fogDrawActionIndex",
"fogDrawActions"
);
for (let peer of Object.values(peers)) {
peer.connection.send({ id: "fogDrawIndex", data: index });
}
}
/**
* Party state
*/
@ -261,7 +271,7 @@ function Game() {
}));
}
if (data.id === "mapDraw") {
addNewMapDrawActions(data.data);
addMapDrawActions(data.data, "mapDrawActionIndex", "mapDrawActions");
}
if (data.id === "mapDrawIndex") {
setMapState((prevMapState) => ({
@ -270,7 +280,7 @@ function Game() {
}));
}
if (data.id === "mapFog") {
addNewFogDrawActions(data.data);
addMapDrawActions(data.data, "fogDrawActionIndex", "fogDrawActions");
}
if (data.id === "mapFogIndex") {
setMapState((prevMapState) => ({
@ -412,9 +422,11 @@ function Game() {
onMapChange={handleMapChange}
onMapStateChange={handleMapStateChange}
onMapDraw={handleMapDraw}
onMapUndo={handleMapUndo}
onMapRedo={handleMapRedo}
onMapDrawUndo={handleMapDrawUndo}
onMapDrawRedo={handleMapDrawRedo}
onFogDraw={handleFogDraw}
onFogDrawUndo={handleFogDrawUndo}
onFogDrawRedo={handleFogDrawRedo}
allowMapDrawing={canEditMapDrawing}
allowFogDrawing={canEditFogDrawing}
allowTokenChange={canEditTokens}