mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
2 Commits
fix/clang-
...
feature/im
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ffa7708a6d | ||
![]() |
2185c25e35 |
@@ -1,25 +1,29 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { I18n } from '@lingui/react';
|
import { I18n } from '@lingui/react';
|
||||||
import { Trans } from '@lingui/macro';
|
import { t, Trans } from '@lingui/macro';
|
||||||
import TranslateIcon from '@material-ui/icons/Translate';
|
import TranslateIcon from '@material-ui/icons/Translate';
|
||||||
import FlatButton from '../../../UI/FlatButton';
|
import FlatButton from '../../../UI/FlatButton';
|
||||||
import { Column } from '../../../UI/Grid';
|
import { Column } from '../../../UI/Grid';
|
||||||
import { LineStackLayout } from '../../../UI/Layout';
|
import { LineStackLayout } from '../../../UI/Layout';
|
||||||
import UserChip from '../../../UI/User/UserChip';
|
import UserChip from '../../../UI/User/UserChip';
|
||||||
|
import ProjectManager from '../../../UI/CustomSvgIcons/ProjectManager';
|
||||||
import Window from '../../../Utils/Window';
|
import Window from '../../../Utils/Window';
|
||||||
import optionalRequire from '../../../Utils/OptionalRequire';
|
import optionalRequire from '../../../Utils/OptionalRequire';
|
||||||
import { useResponsiveWindowWidth } from '../../../UI/Reponsive/ResponsiveWindowMeasurer';
|
import { useResponsiveWindowWidth } from '../../../UI/Reponsive/ResponsiveWindowMeasurer';
|
||||||
import TextButton from '../../../UI/TextButton';
|
import TextButton from '../../../UI/TextButton';
|
||||||
|
import IconButton from '../../../UI/IconButton';
|
||||||
const electron = optionalRequire('electron');
|
const electron = optionalRequire('electron');
|
||||||
|
|
||||||
type Props = {|
|
type Props = {|
|
||||||
|
hasProject: boolean,
|
||||||
onOpenProjectManager: () => void,
|
onOpenProjectManager: () => void,
|
||||||
onOpenProfile: () => void,
|
onOpenProfile: () => void,
|
||||||
onOpenLanguageDialog: () => void,
|
onOpenLanguageDialog: () => void,
|
||||||
|};
|
|};
|
||||||
|
|
||||||
export const HomePageHeader = ({
|
export const HomePageHeader = ({
|
||||||
|
hasProject,
|
||||||
onOpenProjectManager,
|
onOpenProjectManager,
|
||||||
onOpenProfile,
|
onOpenProfile,
|
||||||
onOpenLanguageDialog,
|
onOpenLanguageDialog,
|
||||||
@@ -30,11 +34,21 @@ export const HomePageHeader = ({
|
|||||||
<I18n>
|
<I18n>
|
||||||
{({ i18n }) => (
|
{({ i18n }) => (
|
||||||
<LineStackLayout
|
<LineStackLayout
|
||||||
justifyContent="flex-end"
|
justifyContent="space-between"
|
||||||
alignItems="center"
|
alignItems="center"
|
||||||
noMargin
|
noMargin
|
||||||
expand
|
expand
|
||||||
>
|
>
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
id="main-toolbar-project-manager-button"
|
||||||
|
onClick={onOpenProjectManager}
|
||||||
|
tooltip={t`Project Manager`}
|
||||||
|
color="default"
|
||||||
|
disabled={!hasProject}
|
||||||
|
>
|
||||||
|
<ProjectManager />
|
||||||
|
</IconButton>
|
||||||
<Column>
|
<Column>
|
||||||
<LineStackLayout noMargin alignItems="center">
|
<LineStackLayout noMargin alignItems="center">
|
||||||
{!electron && windowWidth !== 'small' && (
|
{!electron && windowWidth !== 'small' && (
|
||||||
|
@@ -132,13 +132,20 @@ export const HomePage = React.memo<Props>(
|
|||||||
if (setToolbar)
|
if (setToolbar)
|
||||||
setToolbar(
|
setToolbar(
|
||||||
<HomePageHeader
|
<HomePageHeader
|
||||||
|
hasProject={!!project}
|
||||||
onOpenLanguageDialog={onOpenLanguageDialog}
|
onOpenLanguageDialog={onOpenLanguageDialog}
|
||||||
onOpenProfile={onOpenProfile}
|
onOpenProfile={onOpenProfile}
|
||||||
onOpenProjectManager={onOpenProjectManager}
|
onOpenProjectManager={onOpenProjectManager}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[setToolbar, onOpenLanguageDialog, onOpenProfile, onOpenProjectManager]
|
[
|
||||||
|
setToolbar,
|
||||||
|
onOpenLanguageDialog,
|
||||||
|
onOpenProfile,
|
||||||
|
onOpenProjectManager,
|
||||||
|
project,
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
const forceUpdateEditor = React.useCallback(() => {
|
const forceUpdateEditor = React.useCallback(() => {
|
||||||
|
@@ -1,6 +1,10 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import { isMacLike, isMobile } from '../Utils/Platform';
|
import { isMacLike, isMobile } from '../Utils/Platform';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transform a Electron-like accelerator string (https://www.electronjs.org/docs/latest/api/accelerator)
|
||||||
|
* so that it's user friendly.
|
||||||
|
*/
|
||||||
export const adaptAcceleratorString = (accelerator: string): string => {
|
export const adaptAcceleratorString = (accelerator: string): string => {
|
||||||
if (isMobile()) {
|
if (isMobile()) {
|
||||||
return ''; // Do not display accelerators on mobile devices
|
return ''; // Do not display accelerators on mobile devices
|
||||||
@@ -13,13 +17,23 @@ export const adaptAcceleratorString = (accelerator: string): string => {
|
|||||||
.replace(/Alt\+/, '⌥')
|
.replace(/Alt\+/, '⌥')
|
||||||
.replace(/Option\+/, '⌥')
|
.replace(/Option\+/, '⌥')
|
||||||
.replace(/Delete/, '⌦')
|
.replace(/Delete/, '⌦')
|
||||||
.replace(/Backspace/, '⌫');
|
.replace(/Backspace/, '⌫')
|
||||||
|
.replace(/numadd/, '+')
|
||||||
|
.replace(/numsub/, '-')
|
||||||
|
.replace(/num1/, '1')
|
||||||
|
.replace(/num2/, '2')
|
||||||
|
.replace(/num3/, '3');
|
||||||
} else {
|
} else {
|
||||||
return accelerator
|
return accelerator
|
||||||
.replace(/CmdOrCtrl\+/, 'Ctrl+')
|
.replace(/CmdOrCtrl\+/, 'Ctrl+')
|
||||||
.replace(/CommandOrControl\+/, 'Ctrl+')
|
.replace(/CommandOrControl\+/, 'Ctrl+')
|
||||||
.replace(/Super\+/, 'Win+')
|
.replace(/Super\+/, 'Win+')
|
||||||
.replace(/Option\+/, 'Alt+')
|
.replace(/Option\+/, 'Alt+')
|
||||||
.replace(/Delete/, 'DEL');
|
.replace(/Delete/, 'DEL')
|
||||||
|
.replace(/numadd/, '+')
|
||||||
|
.replace(/numsub/, '-')
|
||||||
|
.replace(/num1/, '1')
|
||||||
|
.replace(/num2/, '2')
|
||||||
|
.replace(/num3/, '3');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import React, { useState, useRef, useCallback } from 'react';
|
// @flow
|
||||||
|
import * as React from 'react';
|
||||||
import Menu from '@material-ui/core/Menu';
|
import Menu from '@material-ui/core/Menu';
|
||||||
import MenuItem from '@material-ui/core/MenuItem';
|
import MenuItem from '@material-ui/core/MenuItem';
|
||||||
import ArrowRightIcon from '@material-ui/icons/ArrowRight';
|
import ArrowRightIcon from '@material-ui/icons/ArrowRight';
|
||||||
@@ -8,18 +9,73 @@ import ListItemIcon from '@material-ui/core/ListItemIcon';
|
|||||||
import ListItemText from '@material-ui/core/ListItemText';
|
import ListItemText from '@material-ui/core/ListItemText';
|
||||||
import Divider from '@material-ui/core/Divider';
|
import Divider from '@material-ui/core/Divider';
|
||||||
import Fade from '@material-ui/core/Fade';
|
import Fade from '@material-ui/core/Fade';
|
||||||
|
import makeStyles from '@material-ui/styles/makeStyles';
|
||||||
|
import { adaptAcceleratorString } from '../AcceleratorString';
|
||||||
|
import { type MenuItemTemplate } from './Menu.flow';
|
||||||
|
|
||||||
|
const useStyles = makeStyles({
|
||||||
|
popOverRoot: {
|
||||||
|
// Put a `pointer-events: none` on the root of the "popover" which is showing
|
||||||
|
// submenus as only the menu is supposed to receive clicks.
|
||||||
|
pointerEvents: 'none',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const styles = {
|
const styles = {
|
||||||
menuItemWithSubMenu: { justifyContent: 'space-between' },
|
|
||||||
divider: { marginLeft: 16, marginRight: 16 },
|
divider: { marginLeft: 16, marginRight: 16 },
|
||||||
|
labelWithAccelerator: {
|
||||||
|
width: '100%',
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
},
|
||||||
|
accelerator: { opacity: 0.65, marginLeft: 16 },
|
||||||
|
menuItemWithSubMenu: { height: 32, justifyContent: 'space-between' },
|
||||||
|
menuItem: {
|
||||||
|
// Force every menu item to have the same height, even if it's a submenu
|
||||||
|
// or if it has an icon.
|
||||||
|
height: 32,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const SubMenuItem = ({ item, buildFromTemplate }) => {
|
const SubMenuItem = ({ item, buildFromTemplate }) => {
|
||||||
const [menuOpen, setMenuOpen] = useState(false);
|
const popoverStyles = useStyles();
|
||||||
const anchorElement = useRef(null);
|
const currentlyHovering = React.useRef(false);
|
||||||
const setAnchorElement = useCallback(element => {
|
const [anchorElement, setAnchorElement] = React.useState(null);
|
||||||
anchorElement.current = element;
|
|
||||||
}, []);
|
const handleOpen = event => {
|
||||||
|
// $FlowFixMe - even if not defined, not a problem.
|
||||||
|
if (item.enabled === false) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!anchorElement) {
|
||||||
|
setAnchorElement(event.currentTarget);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function handleHover() {
|
||||||
|
// When we hover the menu item or the submenu, we remember it
|
||||||
|
// so it's not closed.
|
||||||
|
currentlyHovering.current = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClose() {
|
||||||
|
setAnchorElement(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleLeave() {
|
||||||
|
// Unless overwrote in the meantime, we consider that
|
||||||
|
// we're not hovering the menu anymore...
|
||||||
|
currentlyHovering.current = false;
|
||||||
|
|
||||||
|
// ...But give 75ms to the user before closing the menu,
|
||||||
|
// if it the menu or the item was not hovered again in the meantime.
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!currentlyHovering.current) {
|
||||||
|
handleClose();
|
||||||
|
}
|
||||||
|
}, 75);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
@@ -27,27 +83,39 @@ const SubMenuItem = ({ item, buildFromTemplate }) => {
|
|||||||
dense
|
dense
|
||||||
style={styles.menuItemWithSubMenu}
|
style={styles.menuItemWithSubMenu}
|
||||||
key={item.label}
|
key={item.label}
|
||||||
disabled={item.enabled === false}
|
disabled={
|
||||||
onClick={event => {
|
// $FlowFixMe - even if not defined, not a problem.
|
||||||
if (item.enabled === false) {
|
item.enabled === false
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
onClick={handleOpen}
|
||||||
if (!anchorElement.current) {
|
onPointerOver={handleOpen}
|
||||||
setAnchorElement(event.currentTarget);
|
onPointerLeave={handleLeave}
|
||||||
}
|
|
||||||
|
|
||||||
setMenuOpen(!menuOpen);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{item.label}
|
{item.label}
|
||||||
<ArrowRightIcon ref={anchorElement} />
|
<ArrowRightIcon />
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<Menu
|
<Menu
|
||||||
open={menuOpen}
|
open={!!anchorElement}
|
||||||
anchorEl={anchorElement.current}
|
anchorEl={anchorElement}
|
||||||
onClose={() => setMenuOpen(false)}
|
onClose={handleClose}
|
||||||
TransitionComponent={Fade}
|
TransitionComponent={Fade}
|
||||||
|
MenuListProps={{
|
||||||
|
onPointerEnter: handleHover,
|
||||||
|
onPointerLeave: handleLeave,
|
||||||
|
|
||||||
|
// Only the menu, when shown, can receive clicks
|
||||||
|
// (not the background, see `popoverStyles.popOverRoot`).
|
||||||
|
style: { pointerEvents: 'auto' },
|
||||||
|
}}
|
||||||
|
getContentAnchorEl={
|
||||||
|
// Counterintuitive, but necessary
|
||||||
|
// as per https://github.com/mui/material-ui/issues/7961#issuecomment-326116559.
|
||||||
|
null
|
||||||
|
}
|
||||||
|
anchorOrigin={{ horizontal: 'right', vertical: 'top' }}
|
||||||
|
PopoverClasses={{
|
||||||
|
root: popoverStyles.popOverRoot,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{buildFromTemplate(item.submenu)}
|
{buildFromTemplate(item.submenu)}
|
||||||
</Menu>
|
</Menu>
|
||||||
@@ -70,19 +138,19 @@ const SubMenuItem = ({ item, buildFromTemplate }) => {
|
|||||||
* - submenu
|
* - submenu
|
||||||
*/
|
*/
|
||||||
export default class MaterialUIMenuImplementation {
|
export default class MaterialUIMenuImplementation {
|
||||||
constructor({ onClose }) {
|
_onClose: () => void;
|
||||||
|
constructor({ onClose }: {| onClose: () => void |}) {
|
||||||
this._onClose = onClose;
|
this._onClose = onClose;
|
||||||
}
|
}
|
||||||
|
|
||||||
buildFromTemplate(template) {
|
buildFromTemplate(template: Array<MenuItemTemplate>) {
|
||||||
return template
|
return template
|
||||||
.map((item, id) => {
|
.map((item, id) => {
|
||||||
if (item.visible === false) return null;
|
if (item.visible === false) return null;
|
||||||
|
|
||||||
// Accelerator is not implemented for Material-UI menus
|
const accelerator = item.accelerator
|
||||||
// const accelerator = item.accelerator
|
? adaptAcceleratorString(item.accelerator)
|
||||||
// ? adaptAcceleratorString(item.accelerator)
|
: undefined;
|
||||||
// : undefined;
|
|
||||||
|
|
||||||
if (item.type === 'separator') {
|
if (item.type === 'separator') {
|
||||||
return <Divider key={'separator' + id} style={styles.divider} />;
|
return <Divider key={'separator' + id} style={styles.divider} />;
|
||||||
@@ -91,8 +159,14 @@ export default class MaterialUIMenuImplementation {
|
|||||||
<MenuItem
|
<MenuItem
|
||||||
dense
|
dense
|
||||||
key={'checkbox' + item.label}
|
key={'checkbox' + item.label}
|
||||||
checked={item.checked}
|
checked={
|
||||||
disabled={item.enabled === false}
|
// $FlowFixMe - existence should be inferred by Flow.
|
||||||
|
item.checked
|
||||||
|
}
|
||||||
|
disabled={
|
||||||
|
// $FlowFixMe - existence should be inferred by Flow.
|
||||||
|
item.enabled === false
|
||||||
|
}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (item.enabled === false) {
|
if (item.enabled === false) {
|
||||||
return;
|
return;
|
||||||
@@ -103,6 +177,7 @@ export default class MaterialUIMenuImplementation {
|
|||||||
}
|
}
|
||||||
this._onClose();
|
this._onClose();
|
||||||
}}
|
}}
|
||||||
|
style={styles.menuItem}
|
||||||
>
|
>
|
||||||
<ListItemIcon>
|
<ListItemIcon>
|
||||||
{item.checked ? <CheckBoxIcon /> : <CheckBoxOutlineBlankIcon />}
|
{item.checked ? <CheckBoxIcon /> : <CheckBoxOutlineBlankIcon />}
|
||||||
@@ -134,8 +209,16 @@ export default class MaterialUIMenuImplementation {
|
|||||||
this._onClose();
|
this._onClose();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
style={styles.menuItem}
|
||||||
>
|
>
|
||||||
{item.label}
|
{!accelerator ? (
|
||||||
|
item.label
|
||||||
|
) : (
|
||||||
|
<div style={styles.labelWithAccelerator}>
|
||||||
|
<span>{item.label}</span>
|
||||||
|
<span style={styles.accelerator}>{accelerator}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user