Skip to content

Commit 2599cbf

Browse files
committed
hide mask sources and identify them
1 parent 679106d commit 2599cbf

5 files changed

Lines changed: 150 additions & 44 deletions

File tree

src/components/LayerSelectOption.js

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* global document */
22

33
import React from "react"
4-
import { useSelector, useStore, shallowEqual } from "react-redux"
4+
import { useSelector, useStore, useDispatch, shallowEqual } from "react-redux"
55
import Col from "react-bootstrap/Col"
66
import Row from "react-bootstrap/Row"
77
import Form from "react-bootstrap/Form"
@@ -10,12 +10,14 @@ import {
1010
selectLayerIds,
1111
selectLayerById,
1212
selectLayerVertices,
13+
updateLayer,
1314
} from "@/features/layers/layersSlice"
1415
import { dimensions } from "@/common/geometry"
1516
import { getShape } from "@/features/shapes/shapeFactory"
1617

1718
const LayerSelectOption = ({ options, optionKey, data, onChange, index }) => {
1819
const store = useStore()
20+
const dispatch = useDispatch()
1921
const option = options[optionKey]
2022
const currentChoice = data[optionKey]
2123
const effectLayerId = data.layerId
@@ -43,9 +45,7 @@ const LayerSelectOption = ({ options, optionKey, data, onChange, index }) => {
4345
{ value: "", label: "(none)" },
4446
...layers.map((layer) => ({
4547
value: layer.id,
46-
label:
47-
(layer.name || `Layer ${layer.id.slice(0, 6)}`) +
48-
(layer.visible ? "" : " (hidden)"),
48+
label: layer.name || `Layer ${layer.id.slice(0, 6)}`,
4949
})),
5050
]
5151

@@ -71,6 +71,13 @@ const LayerSelectOption = ({ options, optionKey, data, onChange, index }) => {
7171
}
7272
}
7373

74+
if (currentChoice && currentChoice !== value) {
75+
dispatch(updateLayer({ id: currentChoice, visible: true }))
76+
}
77+
if (value) {
78+
dispatch(updateLayer({ id: value, visible: false }))
79+
}
80+
7481
if (option.onChange !== undefined) {
7582
attrs = option.onChange(null, attrs, data)
7683
}

src/features/effects/effectsSlice.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,18 @@ export const selectEffectSelectionVertices = createCachedSelector(
151151
return instance.getSelectionVertices(effect)
152152
},
153153
)(cachedByIdDeepEqual)
154+
155+
export const selectMaskSources = createSelector(
156+
selectAllEffects,
157+
(effects) => {
158+
const map = new Map()
159+
160+
effects
161+
.filter(
162+
(e) => e.type === "mask" && e.maskMachine === "layer" && e.maskLayerId,
163+
)
164+
.forEach((e) => map.set(e.maskLayerId, e.layerId))
165+
166+
return map
167+
},
168+
)

src/features/layers/LayerEditor.js

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,25 @@ import {
1616
} from "react-icons/ai"
1717
import ModelOption from "@/components/ModelOption"
1818
import { getShapeSelectOptions } from "@/features/shapes/shapeFactory"
19-
import { updateLayer, changeModelType } from "./layersSlice"
19+
import {
20+
updateLayer,
21+
changeModelType,
22+
selectSelectedLayer,
23+
selectLayerById,
24+
} from "./layersSlice"
2025
import Layer from "./Layer"
2126
import EffectManager from "@/features/effects/EffectManager"
22-
import { selectSelectedLayer } from "./layersSlice"
27+
import { selectMaskSources } from "@/features/effects/effectsSlice"
2328

2429
const LayerEditor = () => {
2530
const { t } = useTranslation()
2631
const dispatch = useDispatch()
2732
const layer = useSelector(selectSelectedLayer)
33+
const maskSources = useSelector(selectMaskSources)
34+
const targetLayerId = maskSources.get(layer.id)
35+
const targetLayer = useSelector((state) =>
36+
targetLayerId ? selectLayerById(state, targetLayerId) : null,
37+
)
2838
const instance = new Layer(layer.type)
2939
const model = instance.model
3040
const layerOptions = instance.getOptions()
@@ -139,11 +149,19 @@ const LayerEditor = () => {
139149

140150
return (
141151
<div className="overflow-visible flex-grow-1">
142-
<div className="px-3 pt-3 border-top border-secondary">
143-
{renderOption({ optionKey: "name" })}
144-
{renderedModelSelection}
145-
{renderedModelOptions}
146-
{renderOption({ optionKey: "connectionMethod" })}
152+
<div className="border-top border-secondary">
153+
{targetLayer && (
154+
<div className="alert alert-warning mb-0">
155+
{t("Mask source for")} the <span className="fst-italic">{targetLayer.name}</span> layer (hidden)
156+
</div>
157+
)}
158+
159+
<div className="pt-3 px-3">
160+
{renderOption({ optionKey: "name" })}
161+
{renderedModelSelection}
162+
{renderedModelOptions}
163+
{renderOption({ optionKey: "connectionMethod" })}
164+
</div>
147165
</div>
148166
{model.canTransform(layer) && (
149167
<div className="px-3 py-2">
@@ -230,7 +248,7 @@ const LayerEditor = () => {
230248
</div>
231249
)}
232250

233-
<div className="border-top border-secondary px-3 pt-1">
251+
<div className="border-top border-secondary px-3 pt-1 pb-4">
234252
<EffectManager />
235253
</div>
236254

src/features/layers/LayerList.js

Lines changed: 75 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import { useTranslation } from "react-i18next"
33
import Button from "react-bootstrap/Button"
44
import ListGroup from "react-bootstrap/ListGroup"
55
import { Tooltip } from "react-tooltip"
6-
import { FaEye, FaEyeSlash } from "react-icons/fa"
7-
import { useSelector, useDispatch } from "react-redux"
6+
import { FaEye, FaEyeSlash, FaMask } from "react-icons/fa"
7+
import { useSelector, useDispatch, useStore } from "react-redux"
88
import { DndContext, useSensor, useSensors, PointerSensor } from "@dnd-kit/core"
99
import { restrictToVerticalAxis } from "@dnd-kit/modifiers"
1010
import {
@@ -20,18 +20,26 @@ import {
2020
selectSelectedLayer,
2121
selectNumLayers,
2222
selectAllLayers,
23+
selectLayerIds,
2324
updateLayer,
2425
} from "@/features/layers/layersSlice"
26+
import {
27+
selectMaskSources,
28+
selectAllEffects,
29+
updateEffect,
30+
} from "@/features/effects/effectsSlice"
2531

2632
const LayerRow = React.memo(function LayerRow({
2733
current,
2834
selected,
2935
numLayers,
3036
layer,
37+
maskTargetName,
3138
handleLayerSelected,
3239
handleToggleLayerVisible,
3340
t,
3441
}) {
42+
const usedAsMask = !!maskTargetName
3543
const { name, id, visible } = layer
3644
const activeClass = current ? "active" : selected ? "selected" : ""
3745
const dragClass = numLayers > 1 ? "cursor-move" : ""
@@ -63,23 +71,30 @@ const LayerRow = React.memo(function LayerRow({
6371
onClick={handleLayerSelected}
6472
>
6573
<div className="layer-left">
66-
<Button
67-
className="layer-button"
68-
variant="light"
69-
data-id={id}
70-
data-tooltip-content={visible ? t("Hide layer") : t("Show layer")}
71-
data-tooltip-id={tooltipId}
72-
data-tooltip-place="top-end"
73-
onClick={(e) => {
74-
handleToggleLayerVisible(id, layer.visible)
75-
}}
76-
>
77-
{visible ? <FaEye size="0.8em" /> : <FaEyeSlash size="0.8em" />}
78-
</Button>
74+
{usedAsMask ? (
75+
<span className="layer-icon">
76+
<FaMask />
77+
</span>
78+
) : (
79+
<Button
80+
className="layer-button"
81+
variant="light"
82+
data-id={id}
83+
data-tooltip-content={visible ? t("Hide layer") : t("Show layer")}
84+
data-tooltip-id={tooltipId}
85+
data-tooltip-place="top-end"
86+
onClick={(e) => {
87+
handleToggleLayerVisible(id, layer.visible)
88+
}}
89+
>
90+
{visible ? <FaEye size="0.8em" /> : <FaEyeSlash size="0.8em" />}
91+
</Button>
92+
)}
7993
</div>
8094

8195
<div className="d-flex no-select flex-grow-1 align-items-center">
82-
<div className="flex-grow-1">{name}</div>
96+
<div>{name}</div>
97+
<div className="flex-grow-1" />
8398
<span
8499
className="me-2"
85100
style={{ fontSize: "80%" }}
@@ -94,6 +109,7 @@ const LayerRow = React.memo(function LayerRow({
94109

95110
const LayerList = () => {
96111
const { t } = useTranslation()
112+
const store = useStore()
97113
// row has to be dragged 3 pixels before dragging starts; this allows the buttons
98114
// on the row to work properly.
99115
const sensors = useSensors(
@@ -108,6 +124,9 @@ const LayerList = () => {
108124
const selectedLayer = useSelector(selectSelectedLayer)
109125
const numLayers = useSelector(selectNumLayers)
110126
const layers = useSelector(selectAllLayers)
127+
const maskSources = useSelector(selectMaskSources)
128+
129+
const layersById = Object.fromEntries(layers.map((l) => [l.id, l]))
111130

112131
const handleLayerSelected = useCallback(
113132
(event) => {
@@ -139,9 +158,28 @@ const LayerList = () => {
139158
const oldIndex = layers.findIndex((layer) => layer.id === active.id)
140159
const newIndex = layers.findIndex((layer) => layer.id === over.id)
141160
dispatch(moveLayer({ oldIndex, newIndex }))
161+
162+
const state = store.getState()
163+
const newLayerIds = selectLayerIds(state)
164+
const effects = selectAllEffects(state)
165+
166+
effects
167+
.filter(
168+
(e) =>
169+
e.type === "mask" && e.maskMachine === "layer" && e.maskLayerId,
170+
)
171+
.forEach((effect) => {
172+
const targetIdx = newLayerIds.indexOf(effect.layerId)
173+
const sourceIdx = newLayerIds.indexOf(effect.maskLayerId)
174+
175+
if (sourceIdx === -1 || sourceIdx >= targetIdx) {
176+
dispatch(updateLayer({ id: effect.maskLayerId, visible: true }))
177+
dispatch(updateEffect({ id: effect.id, maskLayerId: null }))
178+
}
179+
})
142180
}
143181
},
144-
[dispatch, layers],
182+
[dispatch, layers, store],
145183
)
146184

147185
return (
@@ -161,20 +199,26 @@ const LayerList = () => {
161199
id="layers"
162200
style={{ maxHeight: "200px" }}
163201
>
164-
{layers.map((layer, index) => (
165-
<LayerRow
166-
id={layer.id}
167-
key={layer.id}
168-
current={currentLayerId === layer.id}
169-
selected={selectedLayer.id === layer.id}
170-
numLayers={numLayers}
171-
layer={layer}
172-
handleLayerSelected={handleLayerSelected}
173-
handleToggleLayerVisible={handleToggleLayerVisible}
174-
index={index}
175-
t={t}
176-
/>
177-
))}
202+
{layers.map((layer, index) => {
203+
const targetLayerId = maskSources.get(layer.id)
204+
const targetLayer = targetLayerId && layersById[targetLayerId]
205+
206+
return (
207+
<LayerRow
208+
id={layer.id}
209+
key={layer.id}
210+
current={currentLayerId === layer.id}
211+
selected={selectedLayer.id === layer.id}
212+
numLayers={numLayers}
213+
layer={layer}
214+
maskTargetName={targetLayer?.name}
215+
handleLayerSelected={handleLayerSelected}
216+
handleToggleLayerVisible={handleToggleLayerVisible}
217+
index={index}
218+
t={t}
219+
/>
220+
)
221+
})}
178222
</ListGroup>
179223
</div>
180224
</SortableContext>

src/features/layers/LayerManager.scss

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
padding-right: 0.5rem !important;
66

77
.active.list-group-item & {
8-
color: white;
8+
color: white !important;
99
background-color: inherit;
1010
}
1111

@@ -14,12 +14,34 @@
1414
}
1515
}
1616

17+
.layer-icon {
18+
color: #6c757d;
19+
padding: 0.5rem 0.5rem;
20+
display: inline-flex;
21+
align-items: center;
22+
23+
> svg {
24+
width: 16px;
25+
height: 26px;
26+
}
27+
28+
.active.list-group-item & {
29+
color: white;
30+
}
31+
}
32+
1733
.cursor-move {
1834
cursor: move;
1935
}
2036

2137
#layers {
2238
.list-group-item {
2339
padding: 0.5rem 0.25rem;
40+
41+
&.layer-hidden {
42+
> div {
43+
opacity: 0.7;
44+
}
45+
}
2446
}
2547
}

0 commit comments

Comments
 (0)