mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Improve new scrollbars on scene editor canvas (#4411)
Do not show in changelog
This commit is contained in:
@@ -9,12 +9,12 @@ import { useScreenType } from '../UI/Reponsive/ScreenTypeMeasurer';
|
||||
import { FullSizeMeasurer } from '../UI/FullSizeMeasurer';
|
||||
import useForceUpdate from '../Utils/UseForceUpdate';
|
||||
import { useDebounce } from '../Utils/UseDebounce';
|
||||
import Rectangle from '../Utils/Rectangle';
|
||||
|
||||
const SCROLLBAR_DETECTION_WIDTH = 50;
|
||||
const SCROLLBAR_TRACK_WIDTH = 16;
|
||||
// Those scrollbar dimensions should be the same as in the CSS file Scrollbar.css
|
||||
const SCROLLBAR_THUMB_WIDTH = 8;
|
||||
const SCROLLBAR_SIZE = 200;
|
||||
const SCROLLBAR_MARGIN = (SCROLLBAR_TRACK_WIDTH - SCROLLBAR_THUMB_WIDTH) / 2;
|
||||
|
||||
const THROTTLE_TIME = 1000 / 60; // 60 FPS
|
||||
|
||||
@@ -22,56 +22,6 @@ const styles = {
|
||||
container: {
|
||||
overflow: 'hidden',
|
||||
},
|
||||
xScrollbarDetectionZone: {
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
height: SCROLLBAR_DETECTION_WIDTH,
|
||||
},
|
||||
xScrollbarTrack: {
|
||||
position: 'relative',
|
||||
display: 'inline-block',
|
||||
width: '100%',
|
||||
marginTop: SCROLLBAR_DETECTION_WIDTH - SCROLLBAR_TRACK_WIDTH,
|
||||
marginRight: SCROLLBAR_TRACK_WIDTH - SCROLLBAR_MARGIN, // leave some margin for the vertical scrollbar
|
||||
height: SCROLLBAR_TRACK_WIDTH,
|
||||
},
|
||||
xThumb: {
|
||||
position: 'relative',
|
||||
width: SCROLLBAR_SIZE,
|
||||
marginTop: SCROLLBAR_MARGIN,
|
||||
height: SCROLLBAR_THUMB_WIDTH,
|
||||
backgroundColor: '#1D1D26',
|
||||
outline: '2px solid #FAFAFA',
|
||||
opacity: 0.3,
|
||||
borderRadius: 4,
|
||||
},
|
||||
yScrollbarDetectionZone: {
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
width: SCROLLBAR_DETECTION_WIDTH,
|
||||
},
|
||||
yScrollbarTrack: {
|
||||
position: 'relative',
|
||||
display: 'inline-block',
|
||||
height: '100%',
|
||||
marginLeft: SCROLLBAR_DETECTION_WIDTH - SCROLLBAR_TRACK_WIDTH,
|
||||
marginBottom: SCROLLBAR_TRACK_WIDTH - SCROLLBAR_MARGIN, // leave some margin for the vertical scrollbar
|
||||
width: SCROLLBAR_TRACK_WIDTH,
|
||||
},
|
||||
yThumb: {
|
||||
position: 'relative',
|
||||
height: SCROLLBAR_SIZE,
|
||||
marginLeft: SCROLLBAR_MARGIN,
|
||||
width: SCROLLBAR_THUMB_WIDTH,
|
||||
backgroundColor: '#1D1D26',
|
||||
outline: '1px solid #FAFAFA',
|
||||
opacity: 0.3,
|
||||
borderRadius: 4,
|
||||
},
|
||||
};
|
||||
|
||||
type Props = {|
|
||||
@@ -85,28 +35,38 @@ const FullSizeInstancesEditorWithScrollbars = (props: Props) => {
|
||||
const { wrappedEditorRef, ...otherProps } = props;
|
||||
|
||||
const editorRef = React.useRef<?InstancesEditor>(null);
|
||||
const xScrollbarDetectionZone = React.useRef<?HTMLDivElement>(null);
|
||||
const xScrollbarTrack = React.useRef<?HTMLDivElement>(null);
|
||||
const xScrollbarThumb = React.useRef<?HTMLDivElement>(null);
|
||||
const yScrollbarDetectionZone = React.useRef<?HTMLDivElement>(null);
|
||||
const yScrollbarTrack = React.useRef<?HTMLDivElement>(null);
|
||||
const yScrollbarThumb = React.useRef<?HTMLDivElement>(null);
|
||||
|
||||
const showScrollbars = React.useRef(false);
|
||||
const showScrollbars = React.useRef<boolean>(false);
|
||||
const timeoutHidingScrollbarsId = React.useRef<?TimeoutID>(null);
|
||||
const isDragging = React.useRef(false);
|
||||
const isDragging = React.useRef<boolean>(false);
|
||||
|
||||
const xValue = React.useRef(0);
|
||||
const yValue = React.useRef(0);
|
||||
const xMin = React.useRef(-5000);
|
||||
const xMax = React.useRef(5000);
|
||||
const yMin = React.useRef(-5000);
|
||||
const yMax = React.useRef(5000);
|
||||
const canvasWidth = React.useRef(0);
|
||||
const canvasHeight = React.useRef(0);
|
||||
const xValue = React.useRef<number>(0);
|
||||
const yValue = React.useRef<number>(0);
|
||||
const xMin = React.useRef<number>(-5000);
|
||||
const xMax = React.useRef<number>(5000);
|
||||
const yMin = React.useRef<number>(-5000);
|
||||
const yMax = React.useRef<number>(5000);
|
||||
const canvasWidth = React.useRef<number>(0);
|
||||
const canvasHeight = React.useRef<number>(0);
|
||||
|
||||
const forceUpdate = useForceUpdate();
|
||||
|
||||
const canvasRectangle = React.useMemo(() => new Rectangle(), []);
|
||||
|
||||
if (editorRef.current) {
|
||||
const elementBoundingRect = editorRef.current.getBoundingClientRect();
|
||||
canvasRectangle.set({
|
||||
left: elementBoundingRect.left,
|
||||
top: elementBoundingRect.top,
|
||||
right: elementBoundingRect.right - SCROLLBAR_DETECTION_WIDTH,
|
||||
bottom: elementBoundingRect.bottom - SCROLLBAR_DETECTION_WIDTH,
|
||||
});
|
||||
}
|
||||
|
||||
const hideScrollbarsAfterDelay = React.useCallback(
|
||||
() => {
|
||||
if (timeoutHidingScrollbarsId.current) {
|
||||
@@ -124,14 +84,54 @@ const FullSizeInstancesEditorWithScrollbars = (props: Props) => {
|
||||
hideScrollbarsAfterDelay();
|
||||
}, 500);
|
||||
|
||||
// If the mouse gets out of the detection zone, whilst not dragging, hide the scrollbars.
|
||||
const mouseOutDetectionZoneHandler = React.useCallback(
|
||||
(e: MouseEvent) => {
|
||||
if (!isDragging.current) {
|
||||
hideScrollbarsAfterDelay();
|
||||
const showScrollbarsThrottled = throttle(
|
||||
() => {
|
||||
if (!showScrollbars.current) {
|
||||
showScrollbars.current = true;
|
||||
forceUpdate();
|
||||
}
|
||||
if (timeoutHidingScrollbarsId.current) {
|
||||
clearTimeout(timeoutHidingScrollbarsId.current);
|
||||
timeoutHidingScrollbarsId.current = null;
|
||||
}
|
||||
},
|
||||
[hideScrollbarsAfterDelay]
|
||||
1000,
|
||||
{ leading: true, trailing: false }
|
||||
);
|
||||
|
||||
const hideScrollbarsAfterDelayThrottled = throttle(
|
||||
hideScrollbarsAfterDelay,
|
||||
1000,
|
||||
{ leading: true, trailing: false }
|
||||
);
|
||||
|
||||
const onMouseMoveOverInstanceEditor = React.useCallback(
|
||||
(event: MouseEvent) => {
|
||||
if (!editorRef.current) {
|
||||
return;
|
||||
}
|
||||
|
||||
const shouldDisplayScrollBars = !canvasRectangle.containsPoint(
|
||||
event.clientX,
|
||||
event.clientY
|
||||
);
|
||||
|
||||
if (shouldDisplayScrollBars) {
|
||||
if (timeoutHidingScrollbarsId.current) {
|
||||
clearTimeout(timeoutHidingScrollbarsId.current);
|
||||
}
|
||||
if (!showScrollbars.current) showScrollbarsThrottled();
|
||||
} else {
|
||||
if (!isDragging.current && showScrollbars.current) {
|
||||
hideScrollbarsAfterDelayThrottled();
|
||||
}
|
||||
}
|
||||
},
|
||||
[
|
||||
canvasRectangle,
|
||||
hideScrollbarsAfterDelayThrottled,
|
||||
showScrollbarsThrottled,
|
||||
]
|
||||
);
|
||||
|
||||
// When the mouse is moving after dragging the thumb:
|
||||
@@ -192,14 +192,11 @@ const FullSizeInstancesEditorWithScrollbars = (props: Props) => {
|
||||
);
|
||||
|
||||
// When the user releases the thumb, we need to stop listening to mouse move and up events.
|
||||
// In the case the user releases outside of the detection zone, we need to hide the scrollbars.
|
||||
const makeMouseUpXThumbHandler = React.useCallback(
|
||||
mouseMoveHandler =>
|
||||
function mouseUpHandler(e: MouseEvent) {
|
||||
isDragging.current = false;
|
||||
// If the user releases the mouse outside of the detection zone, we want to hide the scrollbars.
|
||||
if (
|
||||
e.target !== xScrollbarDetectionZone.current &&
|
||||
e.target !== xScrollbarTrack.current &&
|
||||
e.target !== xScrollbarThumb.current
|
||||
) {
|
||||
@@ -214,9 +211,7 @@ const FullSizeInstancesEditorWithScrollbars = (props: Props) => {
|
||||
mouseMoveHandler =>
|
||||
function mouseUpHandler(e: MouseEvent) {
|
||||
isDragging.current = false;
|
||||
// If the user releases the mouse outside of the detection zone, we want to hide the scrollbars.
|
||||
if (
|
||||
e.target !== yScrollbarDetectionZone.current &&
|
||||
e.target !== yScrollbarTrack.current &&
|
||||
e.target !== yScrollbarThumb.current
|
||||
) {
|
||||
@@ -284,50 +279,6 @@ const FullSizeInstancesEditorWithScrollbars = (props: Props) => {
|
||||
[mouseDownXThumbHandler, mouseDownYThumbHandler]
|
||||
);
|
||||
|
||||
// When the mouse is over the detection zone, show the scrollbars,
|
||||
// and ensure the timeout to hide them is cleared.
|
||||
const mouseOverDetectionZoneHandler = React.useCallback(
|
||||
(e: MouseEvent) => {
|
||||
if (!showScrollbars.current) {
|
||||
showScrollbars.current = true;
|
||||
forceUpdate();
|
||||
}
|
||||
if (timeoutHidingScrollbarsId.current) {
|
||||
clearTimeout(timeoutHidingScrollbarsId.current);
|
||||
timeoutHidingScrollbarsId.current = null;
|
||||
}
|
||||
},
|
||||
[forceUpdate]
|
||||
);
|
||||
|
||||
// Add the mouse over and out events once on mount.
|
||||
React.useEffect(
|
||||
() => {
|
||||
const xScrollbarDetectionZoneElement = xScrollbarDetectionZone.current;
|
||||
const yScrollbarDetectionZoneElement = yScrollbarDetectionZone.current;
|
||||
if (!xScrollbarDetectionZoneElement || !yScrollbarDetectionZoneElement)
|
||||
return;
|
||||
|
||||
xScrollbarDetectionZoneElement.addEventListener(
|
||||
'mouseover',
|
||||
mouseOverDetectionZoneHandler
|
||||
);
|
||||
xScrollbarDetectionZoneElement.addEventListener(
|
||||
'mouseout',
|
||||
mouseOutDetectionZoneHandler
|
||||
);
|
||||
yScrollbarDetectionZoneElement.addEventListener(
|
||||
'mouseover',
|
||||
mouseOverDetectionZoneHandler
|
||||
);
|
||||
yScrollbarDetectionZoneElement.addEventListener(
|
||||
'mouseout',
|
||||
mouseOutDetectionZoneHandler
|
||||
);
|
||||
},
|
||||
[mouseOverDetectionZoneHandler, mouseOutDetectionZoneHandler]
|
||||
);
|
||||
|
||||
const setAndAdjust = React.useCallback(
|
||||
({
|
||||
newXValue,
|
||||
@@ -376,9 +327,18 @@ const FullSizeInstancesEditorWithScrollbars = (props: Props) => {
|
||||
[setAndAdjust]
|
||||
);
|
||||
|
||||
const onMouseEnterThumb = (event: MouseEvent) => {
|
||||
if (timeoutHidingScrollbarsId.current) {
|
||||
clearTimeout(timeoutHidingScrollbarsId.current);
|
||||
}
|
||||
};
|
||||
const onMouseLeaveThumb = (event: MouseEvent) => {
|
||||
if (!isDragging.current) hideScrollbarsAfterDelay();
|
||||
};
|
||||
|
||||
// Ensure the X Scrollbar doesn't go out of bounds.
|
||||
const minXScrollbarLeftPosition = '0%';
|
||||
const maxXScrollbarLeftPosition = `calc(100% - ${SCROLLBAR_SIZE}px - ${SCROLLBAR_TRACK_WIDTH}px)`;
|
||||
const maxXScrollbarLeftPosition = `calc(100% - ${SCROLLBAR_SIZE}px - ${SCROLLBAR_THUMB_WIDTH}px)`;
|
||||
const expectedXScrollbarLeftPosition = `calc(${((xValue.current -
|
||||
xMin.current) /
|
||||
(xMax.current - xMin.current)) *
|
||||
@@ -388,7 +348,7 @@ const FullSizeInstancesEditorWithScrollbars = (props: Props) => {
|
||||
|
||||
// Ensure the Y Scrollbar doesn't go out of bounds.
|
||||
const minYScrollbarTopPosition = '0%';
|
||||
const maxYScrollbarTopPosition = `calc(100% - ${SCROLLBAR_SIZE}px - ${SCROLLBAR_TRACK_WIDTH}px)`;
|
||||
const maxYScrollbarTopPosition = `calc(100% - ${SCROLLBAR_SIZE}px - ${SCROLLBAR_THUMB_WIDTH}px)`;
|
||||
const expectedYScrollbarTopPosition = `calc(${((yValue.current -
|
||||
yMin.current) /
|
||||
(yMax.current - yMin.current)) *
|
||||
@@ -414,53 +374,65 @@ const FullSizeInstancesEditorWithScrollbars = (props: Props) => {
|
||||
width={width}
|
||||
height={height}
|
||||
screenType={screenType}
|
||||
onMouseMove={onMouseMoveOverInstanceEditor}
|
||||
onMouseLeave={(event: MouseEvent) => {
|
||||
const { relatedTarget } = event;
|
||||
if (!isDragging.current && relatedTarget) {
|
||||
if (
|
||||
// Flow says className is not present in ElementTarget but this piece
|
||||
// of code cannot break.
|
||||
// $FlowFixMe
|
||||
relatedTarget.className &&
|
||||
typeof relatedTarget.className === 'string' &&
|
||||
// Hide only if the mouse is not leaving to go on one of the scrollbars' thumb.
|
||||
// $FlowFixMe
|
||||
!relatedTarget.className.includes('canvas-scrollbar-thumb')
|
||||
) {
|
||||
hideScrollbarsAfterDelay();
|
||||
}
|
||||
}
|
||||
}}
|
||||
{...otherProps}
|
||||
/>
|
||||
)}
|
||||
{screenType !== 'touch' && (
|
||||
<div
|
||||
style={styles.yScrollbarDetectionZone}
|
||||
ref={yScrollbarDetectionZone}
|
||||
style={{
|
||||
// Keep it in the DOM, so we can register the mouse down event.
|
||||
visibility: showScrollbars.current ? 'visible' : 'hidden',
|
||||
}}
|
||||
className="canvas-vertical-scrollbar-track"
|
||||
ref={yScrollbarTrack}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
...styles.yScrollbarTrack,
|
||||
// Keep it in the DOM, so we can register the mouse down event.
|
||||
visibility: showScrollbars.current ? 'visible' : 'hidden',
|
||||
top: yScrollbarTopPosition,
|
||||
}}
|
||||
ref={yScrollbarTrack}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
...styles.yThumb,
|
||||
top: yScrollbarTopPosition,
|
||||
}}
|
||||
ref={yScrollbarThumb}
|
||||
/>
|
||||
</div>
|
||||
className="canvas-scrollbar-thumb canvas-vertical-scrollbar-thumb"
|
||||
ref={yScrollbarThumb}
|
||||
onMouseEnter={onMouseEnterThumb}
|
||||
onMouseLeave={onMouseLeaveThumb}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{screenType !== 'touch' && (
|
||||
<div
|
||||
style={styles.xScrollbarDetectionZone}
|
||||
ref={xScrollbarDetectionZone}
|
||||
style={{
|
||||
// Keep it in the DOM, so we can register the mouse down event.
|
||||
visibility: showScrollbars.current ? 'visible' : 'hidden',
|
||||
}}
|
||||
className="canvas-horizontal-scrollbar-track"
|
||||
ref={xScrollbarTrack}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
...styles.xScrollbarTrack,
|
||||
// Keep it in the DOM, so we can register the mouse down event.
|
||||
visibility: showScrollbars.current ? 'visible' : 'hidden',
|
||||
marginLeft: xScrollbarLeftPosition,
|
||||
}}
|
||||
ref={xScrollbarTrack}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
...styles.xThumb,
|
||||
marginLeft: xScrollbarLeftPosition,
|
||||
}}
|
||||
ref={xScrollbarThumb}
|
||||
/>
|
||||
</div>
|
||||
className="canvas-scrollbar-thumb canvas-horizontal-scrollbar-thumb"
|
||||
ref={xScrollbarThumb}
|
||||
onMouseEnter={onMouseEnterThumb}
|
||||
onMouseLeave={onMouseLeaveThumb}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
@@ -85,6 +85,8 @@ type Props = {|
|
||||
width: number,
|
||||
height: number,
|
||||
onViewPositionChanged: ViewPosition => void,
|
||||
onMouseMove: MouseEvent => void,
|
||||
onMouseLeave: MouseEvent => void,
|
||||
screenType: ScreenType,
|
||||
|};
|
||||
|
||||
@@ -179,16 +181,13 @@ export default class InstancesEditor extends Component<Props> {
|
||||
this.zoomOnCursorBy(-event.deltaY / 5000);
|
||||
} else if (this.keyboardShortcuts.shouldScrollHorizontally()) {
|
||||
const deltaX = event.deltaY / (5 * zoomFactor);
|
||||
this.viewPosition.scrollBy(-deltaX, 0);
|
||||
this.scrollBy(-deltaX, 0);
|
||||
} else {
|
||||
const deltaX = event.deltaX / (5 * zoomFactor);
|
||||
const deltaY = event.deltaY / (5 * zoomFactor);
|
||||
this.viewPosition.scrollBy(deltaX, deltaY);
|
||||
this.scrollBy(deltaX, deltaY);
|
||||
}
|
||||
|
||||
if (this.props.onViewPositionChanged) {
|
||||
this.props.onViewPositionChanged(this.viewPosition);
|
||||
}
|
||||
event.preventDefault();
|
||||
};
|
||||
this.pixiRenderer.view.setAttribute('tabIndex', -1);
|
||||
@@ -208,6 +207,12 @@ export default class InstancesEditor extends Component<Props> {
|
||||
'mouseup',
|
||||
this.keyboardShortcuts.onMouseUp
|
||||
);
|
||||
this.pixiRenderer.view.addEventListener('mousemove', event =>
|
||||
this.props.onMouseMove(event)
|
||||
);
|
||||
this.pixiRenderer.view.addEventListener('mouseout', event => {
|
||||
this.props.onMouseLeave(event);
|
||||
});
|
||||
|
||||
this.pixiContainer = new PIXI.Container();
|
||||
|
||||
@@ -518,13 +523,10 @@ export default class InstancesEditor extends Component<Props> {
|
||||
this.setZoomFactor(this.getZoomFactor() + value);
|
||||
const afterZoomCursorPosition = this.getLastCursorSceneCoordinates();
|
||||
// Compensate for the cursor change in position
|
||||
this.viewPosition.scrollBy(
|
||||
this.scrollBy(
|
||||
beforeZoomCursorPosition[0] - afterZoomCursorPosition[0],
|
||||
beforeZoomCursorPosition[1] - afterZoomCursorPosition[1]
|
||||
);
|
||||
if (this.props.onViewPositionChanged) {
|
||||
this.props.onViewPositionChanged(this.viewPosition);
|
||||
}
|
||||
}
|
||||
|
||||
getZoomFactor = () => {
|
||||
@@ -582,11 +584,7 @@ export default class InstancesEditor extends Component<Props> {
|
||||
const sceneDeltaX = deltaX / this.getZoomFactor();
|
||||
const sceneDeltaY = deltaY / this.getZoomFactor();
|
||||
|
||||
this.viewPosition.scrollBy(-sceneDeltaX, -sceneDeltaY);
|
||||
|
||||
if (this.props.onViewPositionChanged) {
|
||||
this.props.onViewPositionChanged(this.viewPosition);
|
||||
}
|
||||
this.scrollBy(-sceneDeltaX, -sceneDeltaY);
|
||||
} else {
|
||||
this.selectionRectangle.updateSelectionRectangle(x, y);
|
||||
}
|
||||
@@ -716,11 +714,7 @@ export default class InstancesEditor extends Component<Props> {
|
||||
// to move the view, move it, then unpress it and continue to move the instance.
|
||||
// This means that while we're in "_onMoveInstance", we must handle view moving.
|
||||
if (this.keyboardShortcuts.shouldMoveView()) {
|
||||
this.viewPosition.scrollBy(-sceneDeltaX, -sceneDeltaY);
|
||||
|
||||
if (this.props.onViewPositionChanged) {
|
||||
this.props.onViewPositionChanged(this.viewPosition);
|
||||
}
|
||||
this.scrollBy(-sceneDeltaX, -sceneDeltaY);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -812,8 +806,35 @@ export default class InstancesEditor extends Component<Props> {
|
||||
this.props.onInstancesMoved(unlockedSelectedInstances);
|
||||
};
|
||||
|
||||
scrollBy(x: number, y: number) {
|
||||
this.viewPosition.scrollBy(x, y);
|
||||
|
||||
if (this.props.onViewPositionChanged) {
|
||||
this.props.onViewPositionChanged(this.viewPosition);
|
||||
}
|
||||
}
|
||||
|
||||
scrollTo(x: number, y: number) {
|
||||
this.viewPosition.scrollTo(x, y);
|
||||
if (this.props.onViewPositionChanged) {
|
||||
this.props.onViewPositionChanged(this.viewPosition);
|
||||
}
|
||||
}
|
||||
|
||||
fitViewToRectangle(
|
||||
rectangle: Rectangle,
|
||||
{ adaptZoom }: { adaptZoom: boolean }
|
||||
) {
|
||||
const idealZoom = this.viewPosition.fitToRectangle(rectangle);
|
||||
if (adaptZoom) this.setZoomFactor(idealZoom);
|
||||
if (this.props.onViewPositionChanged) {
|
||||
this.props.onViewPositionChanged(this.viewPosition);
|
||||
}
|
||||
}
|
||||
|
||||
getBoundingClientRect() {
|
||||
if (!this.canvasArea) return { left: 0, top: 0, right: 0, bottom: 0 };
|
||||
return this.canvasArea.getBoundingClientRect();
|
||||
}
|
||||
|
||||
zoomToFitContent() {
|
||||
@@ -844,17 +865,14 @@ export default class InstancesEditor extends Component<Props> {
|
||||
// $FlowFixMe - JSFunctor is incompatible with Functor
|
||||
initialInstances.iterateOverInstances(getInstanceRectangle);
|
||||
getInstanceRectangle.delete();
|
||||
if (contentAABB) {
|
||||
const idealZoom = this.viewPosition.fitToRectangle(contentAABB);
|
||||
this.setZoomFactor(idealZoom);
|
||||
}
|
||||
if (contentAABB) this.fitViewToRectangle(contentAABB, { adaptZoom: true });
|
||||
}
|
||||
|
||||
zoomToInitialPosition() {
|
||||
const x = this.props.project.getGameResolutionWidth() / 2;
|
||||
const y = this.props.project.getGameResolutionHeight() / 2;
|
||||
this.viewPosition.scrollTo(x, y);
|
||||
this.setZoomFactor(1);
|
||||
this.scrollTo(x, y);
|
||||
}
|
||||
|
||||
zoomToFitSelection(instances: Array<gdInitialInstance>) {
|
||||
@@ -870,10 +888,7 @@ export default class InstancesEditor extends Component<Props> {
|
||||
instanceMeasurer.getInstanceAABB(instance, new Rectangle())
|
||||
);
|
||||
});
|
||||
const idealZoom = this.viewPosition.fitToRectangle(
|
||||
selectedInstancesRectangle
|
||||
);
|
||||
this.setZoomFactor(idealZoom);
|
||||
this.fitViewToRectangle(selectedInstancesRectangle, { adaptZoom: true });
|
||||
}
|
||||
|
||||
centerViewOnLastInstance(instances: Array<gdInitialInstance>) {
|
||||
@@ -884,10 +899,7 @@ export default class InstancesEditor extends Component<Props> {
|
||||
instances[instances.length - 1],
|
||||
new Rectangle()
|
||||
);
|
||||
this.viewPosition.fitToRectangle(lastInstanceRectangle);
|
||||
if (this.props.onViewPositionChanged) {
|
||||
this.props.onViewPositionChanged(this.viewPosition);
|
||||
}
|
||||
this.fitViewToRectangle(lastInstanceRectangle, { adaptZoom: false });
|
||||
}
|
||||
|
||||
getLastContextMenuSceneCoordinates = () => {
|
||||
|
@@ -23,7 +23,7 @@
|
||||
|
||||
/* Make the horizontal split to have the same thickness as the vertical */
|
||||
.mosaic-gd-theme .mosaic-split.-column {
|
||||
margin-top: -2px;
|
||||
margin-top: -3px;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
|
@@ -43,9 +43,10 @@
|
||||
|
||||
.mosaic-gd-theme .mosaic-split.-column:hover .mosaic-split-line {
|
||||
background-color: var(--mosaic-toolbar-border-color-hover);
|
||||
transform: scaleY(2);
|
||||
transform: scaleY(2.5);
|
||||
box-shadow: none !important;
|
||||
cursor: ns-resize;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
/* Separator draggable background (vertical split) */
|
||||
@@ -60,7 +61,8 @@
|
||||
|
||||
.mosaic-gd-theme .mosaic-split.-row:hover .mosaic-split-line {
|
||||
background-color: var(--mosaic-toolbar-border-color-hover);
|
||||
transform: scaleX(2);
|
||||
transform: scaleX(2.5);
|
||||
box-shadow: none !important;
|
||||
cursor: ew-resize;
|
||||
z-index: 5;
|
||||
}
|
||||
|
@@ -1,54 +1,106 @@
|
||||
/* The default scrollbar for the app */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track,
|
||||
::-webkit-scrollbar-thumb {
|
||||
border: 1px solid transparent;
|
||||
background-clip: padding-box;
|
||||
border-radius: 4px;
|
||||
border: 1px solid transparent;
|
||||
background-clip: padding-box;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background-color: rgba(0, 0, 0, 0.07);
|
||||
background-color: rgba(0, 0, 0, 0.07);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background-color: #8d8d8dcc;
|
||||
background-color: #8d8d8dcc;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #8d8d8d;
|
||||
background-color: #8d8d8d;
|
||||
}
|
||||
|
||||
/** The scrollbar for tabs or toolbars: either invisible or very thing */
|
||||
.almost-invisible-scrollbar {
|
||||
/** For browsers not supporting -webkit-scrollbar, hide it because even the `thin` option is too large. */
|
||||
scrollbar-width: none;
|
||||
/** For browsers not supporting -webkit-scrollbar, hide it because even the `thin` option is too large. */
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.almost-invisible-scrollbar::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.almost-invisible-scrollbar::-webkit-scrollbar-track,
|
||||
.almost-invisible-scrollbar::-webkit-scrollbar-thumb {
|
||||
border: 1px solid transparent;
|
||||
background-clip: padding-box;
|
||||
border-radius: 4px;
|
||||
border: 1px solid transparent;
|
||||
background-clip: padding-box;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.almost-invisible-scrollbar::-webkit-scrollbar-track {
|
||||
background-color: rgba(0, 0, 0, 0.07);
|
||||
background-color: rgba(0, 0, 0, 0.07);
|
||||
}
|
||||
|
||||
.almost-invisible-scrollbar::-webkit-scrollbar-thumb {
|
||||
background-color: #8d8d8dcc;
|
||||
background-color: #8d8d8dcc;
|
||||
}
|
||||
|
||||
.almost-invisible-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||
background-color: #8d8d8d;
|
||||
background-color: #8d8d8d;
|
||||
}
|
||||
|
||||
/* Manually handled scrollbar for canvas */
|
||||
body {
|
||||
--canvas-scrollbar-border-radius: 4px;
|
||||
--canvas-scrollbar-width: 8px;
|
||||
--canvas-scrollbar-length: 200px;
|
||||
--canvas-horizontal-scrollbar-padding: 3px;
|
||||
--canvas-vertical-scrollbar-padding: 1px;
|
||||
}
|
||||
|
||||
.canvas-scrollbar-thumb {
|
||||
position: relative;
|
||||
background-color: #8d8d8dcc;
|
||||
border: 1px solid transparent;
|
||||
box-sizing: border-box;
|
||||
background-clip: content-box;
|
||||
border-radius: var(--canvas-scrollbar-border-radius);
|
||||
pointer-events: all;
|
||||
}
|
||||
.canvas-scrollbar-thumb:hover,
|
||||
.canvas-scrollbar-thumb:active {
|
||||
background-color: #8d8d8d;
|
||||
}
|
||||
|
||||
.canvas-horizontal-scrollbar-track {
|
||||
position: absolute;
|
||||
left: 0px;
|
||||
right: calc(
|
||||
var(--canvas-scrollbar-width) - var(--canvas-vertical-scrollbar-padding)
|
||||
);
|
||||
bottom: 0px;
|
||||
padding-bottom: var(--canvas-horizontal-scrollbar-padding);
|
||||
pointer-events: none;
|
||||
}
|
||||
.canvas-horizontal-scrollbar-thumb {
|
||||
width: var(--canvas-scrollbar-length);
|
||||
height: var(--canvas-scrollbar-width);
|
||||
}
|
||||
.canvas-vertical-scrollbar-track {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
bottom: calc(
|
||||
var(--canvas-scrollbar-width) - var(--canvas-horizontal-scrollbar-padding)
|
||||
);
|
||||
right: 0px;
|
||||
padding-right: var(--canvas-vertical-scrollbar-padding);
|
||||
pointer-events: none;
|
||||
}
|
||||
.canvas-vertical-scrollbar-thumb {
|
||||
height: var(--canvas-scrollbar-length);
|
||||
width: var(--canvas-scrollbar-width);
|
||||
}
|
||||
|
@@ -91,6 +91,10 @@ export default class Rectangle {
|
||||
}
|
||||
}
|
||||
|
||||
containsPoint(x: number, y: number): boolean {
|
||||
return this.left <= x && this.right > x && this.bottom > y && this.top <= y;
|
||||
}
|
||||
|
||||
toString() {
|
||||
return (
|
||||
'[' +
|
||||
|
Reference in New Issue
Block a user