Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add possibility to paint tilemap with a rectangle selection from the tileset #6977

Merged
merged 17 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import IconButton from '../../UI/IconButton';
import { Line, Column, Spacer, marginsSize } from '../../UI/Grid';
import Text from '../../UI/Text';
import { type UnsavedChanges } from '../../MainFrame/UnsavedChangesContext';
import ScrollView from '../../UI/ScrollView';
import ScrollView, { type ScrollViewInterface } from '../../UI/ScrollView';
import EventsRootVariablesFinder from '../../Utils/EventsRootVariablesFinder';
import VariablesList, {
type HistoryHandler,
Expand Down Expand Up @@ -95,7 +95,7 @@ const CompactInstancePropertiesEditor = ({
onSelectTileMapTile,
}: Props) => {
const forceUpdate = useForceUpdate();

const scrollViewRef = React.useRef<?ScrollViewInterface>(null);
const instance = instances[0];
/**
* TODO: multiple instances support for variables list. Expected behavior should be:
Expand All @@ -105,6 +105,12 @@ const CompactInstancePropertiesEditor = ({
*/
const shouldDisplayVariablesList = instances.length === 1;

const onScrollY = React.useCallback(deltaY => {
if (scrollViewRef.current) {
scrollViewRef.current.scrollBy(deltaY);
}
}, []);

const { object, instanceSchema } = React.useMemo<{|
object?: gdObject,
instanceSchema?: Schema,
Expand Down Expand Up @@ -220,6 +226,7 @@ const CompactInstancePropertiesEditor = ({
scope="scene-editor-instance-properties"
>
<ScrollView
ref={scrollViewRef}
autoHideScrollbar
style={styles.scrollView}
key={instances
Expand Down Expand Up @@ -253,6 +260,7 @@ const CompactInstancePropertiesEditor = ({
onSelectTileMapTile={onSelectTileMapTile}
showPaintingToolbar
allowMultipleSelection={false}
onScrollY={onScrollY}
allowRectangleSelection
interactive
/>
Expand Down
86 changes: 60 additions & 26 deletions newIDE/app/src/InstancesEditor/TileSetVisualizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ const styles = {
position: 'relative',
display: 'flex',
overflow: 'auto',
touchAction: 'none',
},
atlasImage: { flex: 1, imageRendering: 'pixelated' },
icon: { fontSize: 18 },
Expand Down Expand Up @@ -217,6 +218,7 @@ type Props = {|
e: SyntheticEvent<HTMLImageElement>,
atlasResourceName: string
) => void,
onScrollY: number => void,
|};

const TileSetVisualizer = ({
Expand All @@ -229,6 +231,7 @@ const TileSetVisualizer = ({
showPaintingToolbar,
interactive,
onAtlasImageLoaded,
onScrollY,
}: Props) => {
const forceUpdate = useForceUpdate();
const atlasResourceName = objectConfiguration
Expand Down Expand Up @@ -278,9 +281,7 @@ const TileSetVisualizer = ({
x: number,
y: number,
|}>(null);
const [shouldCancelClick, setShouldCancelClick] = React.useState<boolean>(
false
);
const [isLongTouch, setIsLongTouch] = React.useState<boolean>(false);
const tooltipDisplayTimeoutId = React.useRef<?TimeoutID>(null);
const [
rectangularSelectionTilePreview,
Expand Down Expand Up @@ -316,7 +317,6 @@ const TileSetVisualizer = ({

const displayTileIdTooltip = React.useCallback(
(e: ClientCoordinates) => {
setShouldCancelClick(true);
if (!displayedTileSize || isBadlyConfigured) return;

const imageCoordinates = getImageCoordinatesFromPointerEvent(e);
Expand All @@ -341,7 +341,15 @@ const TileSetVisualizer = ({
[displayedTileSize, columnCount, rowCount, isBadlyConfigured]
);

const longTouchProps = useLongTouch(displayTileIdTooltip);
const handleLongTouch = React.useCallback(
(e: ClientCoordinates) => {
setIsLongTouch(true);
displayTileIdTooltip(e);
},
[displayTileIdTooltip]
);

const longTouchProps = useLongTouch(handleLongTouch, {doNotCancelOnScroll: true});

React.useEffect(
() => {
Expand All @@ -356,7 +364,12 @@ const TileSetVisualizer = ({
(event: PointerEvent) => {
if (isBadlyConfigured) return;
if (event.pointerType === 'touch') {
setTouchStartCoordinates({ x: event.pageX, y: event.pageY });
const coordinates = getImageCoordinatesFromPointerEvent(event);
if (!coordinates) return;
setTouchStartCoordinates({
x: coordinates.mouseX,
y: coordinates.mouseY,
});
}
const imageCoordinates = getImageCoordinatesFromPointerEvent(event);
if (!imageCoordinates) return;
Expand All @@ -374,11 +387,33 @@ const TileSetVisualizer = ({
isBadlyConfigured ||
!clickStartCoordinates ||
!displayedTileSize ||
(!allowMultipleSelection && !allowRectangleSelection) ||
event.pointerType === 'touch'
(!allowMultipleSelection && !allowRectangleSelection)
) {
return;
}

let startCoordinates = clickStartCoordinates;

const isTouchDevice = event.pointerType === 'touch';

if (isTouchDevice) {
if (!touchStartCoordinates) return;

if (isLongTouch) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a comment explaining what this does:

"Distinguish between a long touch (to multi select tiles) and a scroll"
(for example)

startCoordinates = touchStartCoordinates;
} else {
const coordinates = getImageCoordinatesFromPointerEvent(event);
if (!coordinates) return;
if (tilesetContainerRef.current) {
const deltaY = -event.movementY;
const deltaX =
touchStartCoordinates.x - coordinates.mouseXWithoutScrollLeft;
tilesetContainerRef.current.scrollLeft = deltaX;
onScrollY(deltaY);
}
return;
}
}
const imageCoordinates = getImageCoordinatesFromPointerEvent(event);
if (!imageCoordinates) return;

Expand All @@ -389,10 +424,11 @@ const TileSetVisualizer = ({
rowCount,
displayedTileSize,
});

const { x: startX, y: startY } = getGridCoordinatesFromPointerCoordinates(
{
pointerX: clickStartCoordinates.x,
pointerY: clickStartCoordinates.y,
pointerX: startCoordinates.x,
pointerY: startCoordinates.y,
columnCount,
rowCount,
displayedTileSize,
Expand All @@ -416,28 +452,25 @@ const TileSetVisualizer = ({
allowMultipleSelection,
allowRectangleSelection,
clickStartCoordinates,
isLongTouch,
touchStartCoordinates,
onScrollY,
]
);

const onPointerUp = React.useCallback(
(event: PointerEvent) => {
try {
if (!displayedTileSize || isBadlyConfigured) return;
if (shouldCancelClick) {
setShouldCancelClick(false);
return;
}

let isTouchDevice = false;
const isTouchDevice = event.pointerType === 'touch';
let startCoordinates = clickStartCoordinates;

if (event.pointerType === 'touch') {
isTouchDevice = true;
if (
!touchStartCoordinates ||
Math.abs(event.pageX - touchStartCoordinates.x) > 30 ||
Math.abs(event.pageY - touchStartCoordinates.y) > 30
) {
if (isTouchDevice) {
if (!isLongTouch || !touchStartCoordinates) {
return;
} else {
startCoordinates = touchStartCoordinates;
}
}

Expand All @@ -451,14 +484,14 @@ const TileSetVisualizer = ({
rowCount,
displayedTileSize,
});
if (!clickStartCoordinates) return;
if (!startCoordinates) return;

const {
x: startX,
y: startY,
} = getGridCoordinatesFromPointerCoordinates({
pointerX: clickStartCoordinates.x,
pointerY: clickStartCoordinates.y,
pointerX: startCoordinates.x,
pointerY: startCoordinates.y,
columnCount,
rowCount,
displayedTileSize,
Expand Down Expand Up @@ -525,6 +558,7 @@ const TileSetVisualizer = ({
setClickStartCoordinates(null);
setRectangularSelectionTilePreview(null);
setTouchStartCoordinates(null);
setIsLongTouch(false);
}
},
[
Expand All @@ -539,7 +573,7 @@ const TileSetVisualizer = ({
allowMultipleSelection,
allowRectangleSelection,
clickStartCoordinates,
shouldCancelClick,
isLongTouch,
touchStartCoordinates,
]
);
Expand Down