mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
2 Commits
v5.5.225
...
experiment
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c8477629d0 | ||
![]() |
08b0a9ff37 |
@@ -3,24 +3,27 @@ import * as React from 'react';
|
||||
import MenuIcon from '../UI/CustomSvgIcons/Menu';
|
||||
import IconButton from '../UI/IconButton';
|
||||
import GDevelopThemeContext from '../UI/Theme/GDevelopThemeContext';
|
||||
import optionalRequire from '../Utils/OptionalRequire';
|
||||
import { isMacLike } from '../Utils/Platform';
|
||||
import Window, { useWindowControlsOverlayWatcher } from '../Utils/Window';
|
||||
import useForceUpdate from '../Utils/UseForceUpdate';
|
||||
const electron = optionalRequire('electron');
|
||||
import Window from '../Utils/Window';
|
||||
import {
|
||||
TitleBarLeftSafeMargins,
|
||||
TitleBarRightSafeMargins,
|
||||
} from '../UI/TitleBarSafeMargins';
|
||||
|
||||
type Props = {|
|
||||
children: React.Node,
|
||||
toggleProjectManager: () => void,
|
||||
|};
|
||||
|
||||
const DRAGGABLE_PART_CLASS_NAME = 'title-bar-draggable-part';
|
||||
|
||||
const styles = {
|
||||
container: { display: 'flex', flexShrink: 0, alignItems: 'flex-end' },
|
||||
leftSideArea: { alignSelf: 'stretch', flexShrink: 0 },
|
||||
rightSideArea: { alignSelf: 'stretch', flex: 1 },
|
||||
menuIcon: { marginLeft: 4, marginRight: 4 },
|
||||
menuIcon: {
|
||||
marginLeft: 4,
|
||||
marginRight: 4,
|
||||
// Make the icon slightly bigger to be centered on the row, so it aligns
|
||||
// with the project manager icon.
|
||||
width: 34,
|
||||
height: 34,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -30,7 +33,6 @@ export default function TabsTitlebar({
|
||||
children,
|
||||
toggleProjectManager,
|
||||
}: Props) {
|
||||
const forceUpdate = useForceUpdate();
|
||||
const gdevelopTheme = React.useContext(GDevelopThemeContext);
|
||||
const backgroundColor = gdevelopTheme.titlebar.backgroundColor;
|
||||
React.useEffect(
|
||||
@@ -40,41 +42,9 @@ export default function TabsTitlebar({
|
||||
[backgroundColor]
|
||||
);
|
||||
|
||||
// An installed PWA can have window controls displayed as overlay. If supported,
|
||||
// we set up a listener to detect any change and force a refresh that will read
|
||||
// the latest size of the controls.
|
||||
useWindowControlsOverlayWatcher({ onChanged: forceUpdate });
|
||||
|
||||
// macOS displays the "traffic lights" on the left.
|
||||
const isDesktopMacos = !!electron && isMacLike();
|
||||
let leftSideOffset = isDesktopMacos ? 76 : 0;
|
||||
|
||||
const isDesktopWindowsOrLinux = !!electron && !isMacLike();
|
||||
// Windows and Linux have their "window controls" on the right
|
||||
let rightSideOffset = isDesktopWindowsOrLinux ? 150 : 0;
|
||||
|
||||
// An installed PWA can have window controls displayed as overlay,
|
||||
// which we measure here to set the offsets.
|
||||
// $FlowFixMe - this API is not handled by Flow.
|
||||
const { windowControlsOverlay } = navigator;
|
||||
if (windowControlsOverlay) {
|
||||
if (windowControlsOverlay.visible) {
|
||||
const { x, width } = windowControlsOverlay.getTitlebarAreaRect();
|
||||
leftSideOffset = x;
|
||||
rightSideOffset = window.innerWidth - x - width;
|
||||
}
|
||||
}
|
||||
const rightSideAdditionalOffsetToGiveSpaceToDrag = 30;
|
||||
|
||||
return (
|
||||
<div style={{ ...styles.container, backgroundColor }}>
|
||||
<div
|
||||
style={{
|
||||
...styles.leftSideArea,
|
||||
width: leftSideOffset,
|
||||
}}
|
||||
className={DRAGGABLE_PART_CLASS_NAME}
|
||||
/>
|
||||
<TitleBarLeftSafeMargins />
|
||||
<IconButton
|
||||
size="small"
|
||||
// Even if not in the toolbar, keep this ID for backward compatibility for tutorials.
|
||||
@@ -86,14 +56,7 @@ export default function TabsTitlebar({
|
||||
<MenuIcon />
|
||||
</IconButton>
|
||||
{children}
|
||||
<div
|
||||
style={{
|
||||
...styles.rightSideArea,
|
||||
minWidth:
|
||||
rightSideOffset + rightSideAdditionalOffsetToGiveSpaceToDrag,
|
||||
}}
|
||||
className={DRAGGABLE_PART_CLASS_NAME}
|
||||
/>
|
||||
<TitleBarRightSafeMargins />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -21,48 +21,16 @@ import Cross from './CustomSvgIcons/Cross';
|
||||
import IconButton from './IconButton';
|
||||
import { Line } from './Grid';
|
||||
import GDevelopThemeContext from './Theme/GDevelopThemeContext';
|
||||
import optionalRequire from '../Utils/OptionalRequire';
|
||||
import useForceUpdate from '../Utils/UseForceUpdate';
|
||||
import { useWindowControlsOverlayWatcher } from '../Utils/Window';
|
||||
|
||||
import { classNameToStillAllowRenderingInstancesEditor } from './MaterialUISpecificUtil';
|
||||
import {
|
||||
getAvoidSoftKeyboardStyle,
|
||||
useSoftKeyboardBottomOffset,
|
||||
} from './MobileSoftKeyboard';
|
||||
|
||||
const electron = optionalRequire('electron');
|
||||
|
||||
const DRAGGABLE_PART_CLASS_NAME = 'title-bar-draggable-part';
|
||||
|
||||
export const DialogTitleBar = ({
|
||||
backgroundColor,
|
||||
}: {|
|
||||
backgroundColor: string,
|
||||
|}) => {
|
||||
// An installed PWA can have window controls displayed as overlay. If supported,
|
||||
// we set up a listener to detect any change and force a refresh that will read
|
||||
// the latest size of the controls.
|
||||
const forceUpdate = useForceUpdate();
|
||||
useWindowControlsOverlayWatcher({ onChanged: forceUpdate });
|
||||
|
||||
// $FlowFixMe - this API is not handled by Flow.
|
||||
const { windowControlsOverlay } = navigator;
|
||||
|
||||
if (!!electron || (windowControlsOverlay && windowControlsOverlay.visible)) {
|
||||
// We're on the desktop app, or in an installed PWA with window controls displayed
|
||||
// as overlay: we need to display a spacing at the top of the dialog.
|
||||
return (
|
||||
<div
|
||||
className={DRAGGABLE_PART_CLASS_NAME}
|
||||
style={{ height: 38, backgroundColor, flexShrink: 0 }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Not on the desktop app, and not in an installed PWA with window controls displayed
|
||||
// as overlay: no need to display a spacing.
|
||||
return null;
|
||||
};
|
||||
import {
|
||||
TitleBarLeftSafeMargins,
|
||||
TitleBarRightSafeMargins,
|
||||
} from './TitleBarSafeMargins';
|
||||
|
||||
// Default.
|
||||
const dialogPaddingX = 24;
|
||||
@@ -110,6 +78,11 @@ const styles = {
|
||||
// Ensure the title can break on any character, to ensure it's visible on mobile. Especially useful for long emails.
|
||||
overflowWrap: 'anywhere',
|
||||
},
|
||||
closeDialogContainer: {
|
||||
// Ensure this part can be interacted with on macOS, when the dialog is fullscreen and used as PWA.
|
||||
// Otherwise, the close button is not clickable.
|
||||
WebkitAppRegion: 'no-drag',
|
||||
},
|
||||
fixedContentContainer: {
|
||||
paddingBottom: 8,
|
||||
},
|
||||
@@ -402,11 +375,6 @@ const Dialog = ({
|
||||
disableBackdropClick={false}
|
||||
onKeyDown={handleKeyDown}
|
||||
>
|
||||
{isFullScreen && (
|
||||
<DialogTitleBar
|
||||
backgroundColor={gdevelopTheme.titlebar.backgroundColor}
|
||||
/>
|
||||
)}
|
||||
<div style={dialogContainerStyle}>
|
||||
<div
|
||||
style={{
|
||||
@@ -415,18 +383,34 @@ const Dialog = ({
|
||||
}}
|
||||
>
|
||||
<Line noMargin justifyContent="space-between" alignItems="flex-start">
|
||||
<Text noMargin size="section-title">
|
||||
{title}
|
||||
</Text>
|
||||
{onRequestClose && !cannotBeDismissed && (
|
||||
<IconButton
|
||||
onClick={onRequestClose}
|
||||
size="small"
|
||||
disabled={cannotBeDismissed}
|
||||
>
|
||||
<Cross />
|
||||
</IconButton>
|
||||
)}
|
||||
<Line noMargin alignItems="center">
|
||||
{isFullScreen && (
|
||||
<TitleBarLeftSafeMargins
|
||||
backgroundColor={gdevelopTheme.dialog.backgroundColor}
|
||||
/>
|
||||
)}
|
||||
<Text noMargin size="section-title">
|
||||
{title}
|
||||
</Text>
|
||||
</Line>
|
||||
<Line noMargin alignItems="center">
|
||||
{onRequestClose && !cannotBeDismissed && (
|
||||
<div style={styles.closeDialogContainer}>
|
||||
<IconButton
|
||||
onClick={onRequestClose}
|
||||
size="small"
|
||||
disabled={cannotBeDismissed}
|
||||
>
|
||||
<Cross />
|
||||
</IconButton>
|
||||
</div>
|
||||
)}
|
||||
{isFullScreen && (
|
||||
<TitleBarRightSafeMargins
|
||||
backgroundColor={gdevelopTheme.dialog.backgroundColor}
|
||||
/>
|
||||
)}
|
||||
</Line>
|
||||
</Line>
|
||||
</div>
|
||||
{fixedContent && (
|
||||
|
@@ -7,10 +7,10 @@ import Typography from '@material-ui/core/Typography';
|
||||
import Cross from './CustomSvgIcons/Cross';
|
||||
import Tooltip from '@material-ui/core/Tooltip';
|
||||
import { tooltipEnterDelay } from './Tooltip';
|
||||
import { DialogTitleBar } from '../UI/Dialog';
|
||||
import { LineStackLayout } from './Layout';
|
||||
import { TitleBarLeftSafeMargins } from './TitleBarSafeMargins';
|
||||
|
||||
const appBarHeight = 32;
|
||||
const appBarHeight = 38;
|
||||
|
||||
const styles = {
|
||||
appBar: {
|
||||
@@ -20,8 +20,11 @@ const styles = {
|
||||
toolbar: {
|
||||
height: appBarHeight,
|
||||
minHeight: appBarHeight,
|
||||
paddingLeft: 15,
|
||||
paddingRight: 15,
|
||||
paddingLeft: 8,
|
||||
paddingRight: 8,
|
||||
// Ensure this part can be interacted with on macOS, when used as PWA.
|
||||
// Otherwise, the buttons are not clickable.
|
||||
WebkitAppRegion: 'no-drag',
|
||||
},
|
||||
title: {
|
||||
fontSize: '15px',
|
||||
@@ -42,7 +45,6 @@ type Props = {|
|
||||
const DrawerTopBar = (props: Props) => {
|
||||
return (
|
||||
<>
|
||||
<DialogTitleBar backgroundColor="transparent" />
|
||||
<AppBar
|
||||
position="static"
|
||||
style={styles.appBar}
|
||||
@@ -51,6 +53,7 @@ const DrawerTopBar = (props: Props) => {
|
||||
elevation={0}
|
||||
>
|
||||
<Toolbar style={styles.toolbar}>
|
||||
<TitleBarLeftSafeMargins />
|
||||
<LineStackLayout noMargin expand alignItems="center">
|
||||
{props.icon && (
|
||||
<IconButton
|
||||
|
115
newIDE/app/src/UI/TitleBarSafeMargins.js
Normal file
115
newIDE/app/src/UI/TitleBarSafeMargins.js
Normal file
@@ -0,0 +1,115 @@
|
||||
// @flow
|
||||
import * as React from 'react';
|
||||
import { isMacLike } from '../Utils/Platform';
|
||||
import useForceUpdate from '../Utils/UseForceUpdate';
|
||||
import Window, { useWindowControlsOverlayWatcher } from '../Utils/Window';
|
||||
import optionalRequire from '../Utils/OptionalRequire';
|
||||
|
||||
const electron = optionalRequire('electron');
|
||||
|
||||
const DRAGGABLE_PART_CLASS_NAME = 'title-bar-draggable-part';
|
||||
|
||||
const titleBarStyles = {
|
||||
leftSideArea: {
|
||||
alignSelf: 'stretch',
|
||||
flexShrink: 0,
|
||||
},
|
||||
rightSideArea: { alignSelf: 'stretch', flex: 1 },
|
||||
};
|
||||
|
||||
export const TitleBarLeftSafeMargins = ({
|
||||
backgroundColor,
|
||||
}: {|
|
||||
backgroundColor?: string,
|
||||
|}) => {
|
||||
// An installed PWA can have window controls displayed as overlay. If supported,
|
||||
// we set up a listener to detect any change and force a refresh that will read
|
||||
// the latest size of the controls.
|
||||
const forceUpdate = useForceUpdate();
|
||||
useWindowControlsOverlayWatcher({ onChanged: forceUpdate });
|
||||
|
||||
let leftSideOffset = 0;
|
||||
// macOS displays the "traffic lights" on the left.
|
||||
const isDesktopMacosFullScreen =
|
||||
!!electron && isMacLike() && Window.isFullScreen();
|
||||
|
||||
if (isDesktopMacosFullScreen) {
|
||||
// When in full screen on macOS, the "traffic lights" are not in the overlay.
|
||||
leftSideOffset = 0;
|
||||
} else {
|
||||
// Otherwise, the windowControlsOverlay tells us how much space is needed.
|
||||
// This can happen for mac apps, or installed PWA.
|
||||
// $FlowFixMe - this API is not handled by Flow.
|
||||
const { windowControlsOverlay } = navigator;
|
||||
if (windowControlsOverlay) {
|
||||
if (windowControlsOverlay.visible) {
|
||||
const { x } = windowControlsOverlay.getTitlebarAreaRect();
|
||||
leftSideOffset = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (leftSideOffset) {
|
||||
return (
|
||||
<div
|
||||
className={DRAGGABLE_PART_CLASS_NAME}
|
||||
style={{
|
||||
...titleBarStyles.leftSideArea,
|
||||
width: leftSideOffset,
|
||||
backgroundColor: backgroundColor || 'transparent',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Not on the desktop app, and not in an installed PWA with window controls displayed
|
||||
// as overlay: no need to display a spacing.
|
||||
return null;
|
||||
};
|
||||
|
||||
export const TitleBarRightSafeMargins = ({
|
||||
backgroundColor,
|
||||
}: {|
|
||||
backgroundColor?: string,
|
||||
|}) => {
|
||||
// An installed PWA can have window controls displayed as overlay. If supported,
|
||||
// we set up a listener to detect any change and force a refresh that will read
|
||||
// the latest size of the controls.
|
||||
const forceUpdate = useForceUpdate();
|
||||
useWindowControlsOverlayWatcher({ onChanged: forceUpdate });
|
||||
|
||||
const isDesktopWindowsOrLinux = !!electron && !isMacLike();
|
||||
// Windows and Linux have their "window controls" on the right
|
||||
let rightSideOffset = isDesktopWindowsOrLinux ? 150 : 0;
|
||||
|
||||
// An installed PWA can have window controls displayed as overlay,
|
||||
// which we measure here to set the offsets.
|
||||
// $FlowFixMe - this API is not handled by Flow.
|
||||
const { windowControlsOverlay } = navigator;
|
||||
if (windowControlsOverlay) {
|
||||
if (windowControlsOverlay.visible) {
|
||||
const { x, width } = windowControlsOverlay.getTitlebarAreaRect();
|
||||
rightSideOffset = window.innerWidth - x - width;
|
||||
}
|
||||
}
|
||||
|
||||
const rightSideAdditionalOffsetToGiveSpaceToDrag = 30;
|
||||
|
||||
if (rightSideOffset) {
|
||||
return (
|
||||
<div
|
||||
className={DRAGGABLE_PART_CLASS_NAME}
|
||||
style={{
|
||||
...titleBarStyles.rightSideArea,
|
||||
minWidth:
|
||||
rightSideOffset + rightSideAdditionalOffsetToGiveSpaceToDrag,
|
||||
backgroundColor: backgroundColor || 'transparent',
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
// Not on the desktop app, and not in an installed PWA with window controls displayed
|
||||
// as overlay: no need to display a spacing.
|
||||
return null;
|
||||
};
|
@@ -22,6 +22,31 @@ export const POSITIONAL_ARGUMENTS_KEY = '_';
|
||||
|
||||
let currentTitleBarColor: ?string = null;
|
||||
|
||||
const onChangeCallbacks = new Set<() => void>();
|
||||
let debouncedGeometryChange = null;
|
||||
|
||||
const setupWindowControlsOverlayWatcher = () => {
|
||||
if (debouncedGeometryChange) {
|
||||
// Already set up.
|
||||
return;
|
||||
}
|
||||
|
||||
// $FlowFixMe - this API is not handled by Flow.
|
||||
const { windowControlsOverlay } = navigator;
|
||||
|
||||
if (windowControlsOverlay) {
|
||||
debouncedGeometryChange = debounce(() => {
|
||||
for (const callback of onChangeCallbacks) {
|
||||
callback();
|
||||
}
|
||||
}, 20);
|
||||
windowControlsOverlay.addEventListener(
|
||||
'geometrychange',
|
||||
debouncedGeometryChange
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Listen to the changes to the window controls provided by the operating system:
|
||||
*
|
||||
@@ -34,31 +59,16 @@ export const useWindowControlsOverlayWatcher = ({
|
||||
}: {|
|
||||
onChanged: () => void,
|
||||
|}) => {
|
||||
// $FlowFixMe - this API is not handled by Flow.
|
||||
const { windowControlsOverlay } = navigator;
|
||||
setupWindowControlsOverlayWatcher();
|
||||
|
||||
React.useEffect(
|
||||
() => {
|
||||
let listenerCallback = null;
|
||||
if (windowControlsOverlay) {
|
||||
listenerCallback = debounce(() => {
|
||||
onChanged();
|
||||
}, 50);
|
||||
windowControlsOverlay.addEventListener(
|
||||
'geometrychange',
|
||||
listenerCallback
|
||||
);
|
||||
}
|
||||
onChangeCallbacks.add(onChanged);
|
||||
return () => {
|
||||
if (listenerCallback) {
|
||||
windowControlsOverlay.removeEventListener(
|
||||
'geometrychange',
|
||||
listenerCallback
|
||||
);
|
||||
}
|
||||
onChangeCallbacks.delete(onChanged);
|
||||
};
|
||||
},
|
||||
[onChanged, windowControlsOverlay]
|
||||
[onChanged]
|
||||
);
|
||||
};
|
||||
|
||||
@@ -372,4 +382,11 @@ export default class Window {
|
||||
return false; // Assume we're not in development mode. Might be incorrect but better not consider production as development.
|
||||
}
|
||||
}
|
||||
|
||||
static isFullScreen(): boolean {
|
||||
if (!remote) return false;
|
||||
|
||||
const browserWindow = remote.getCurrentWindow();
|
||||
return browserWindow.isFullScreen();
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user