mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
2 Commits
v5.4.207
...
feature/im
Author | SHA1 | Date | |
---|---|---|---|
![]() |
ffa7708a6d | ||
![]() |
2185c25e35 |
@@ -1,25 +1,29 @@
|
||||
// @flow
|
||||
import * as React from '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 FlatButton from '../../../UI/FlatButton';
|
||||
import { Column } from '../../../UI/Grid';
|
||||
import { LineStackLayout } from '../../../UI/Layout';
|
||||
import UserChip from '../../../UI/User/UserChip';
|
||||
import ProjectManager from '../../../UI/CustomSvgIcons/ProjectManager';
|
||||
import Window from '../../../Utils/Window';
|
||||
import optionalRequire from '../../../Utils/OptionalRequire';
|
||||
import { useResponsiveWindowWidth } from '../../../UI/Reponsive/ResponsiveWindowMeasurer';
|
||||
import TextButton from '../../../UI/TextButton';
|
||||
import IconButton from '../../../UI/IconButton';
|
||||
const electron = optionalRequire('electron');
|
||||
|
||||
type Props = {|
|
||||
hasProject: boolean,
|
||||
onOpenProjectManager: () => void,
|
||||
onOpenProfile: () => void,
|
||||
onOpenLanguageDialog: () => void,
|
||||
|};
|
||||
|
||||
export const HomePageHeader = ({
|
||||
hasProject,
|
||||
onOpenProjectManager,
|
||||
onOpenProfile,
|
||||
onOpenLanguageDialog,
|
||||
@@ -30,11 +34,21 @@ export const HomePageHeader = ({
|
||||
<I18n>
|
||||
{({ i18n }) => (
|
||||
<LineStackLayout
|
||||
justifyContent="flex-end"
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
noMargin
|
||||
expand
|
||||
>
|
||||
<IconButton
|
||||
size="small"
|
||||
id="main-toolbar-project-manager-button"
|
||||
onClick={onOpenProjectManager}
|
||||
tooltip={t`Project Manager`}
|
||||
color="default"
|
||||
disabled={!hasProject}
|
||||
>
|
||||
<ProjectManager />
|
||||
</IconButton>
|
||||
<Column>
|
||||
<LineStackLayout noMargin alignItems="center">
|
||||
{!electron && windowWidth !== 'small' && (
|
||||
|
@@ -132,13 +132,20 @@ export const HomePage = React.memo<Props>(
|
||||
if (setToolbar)
|
||||
setToolbar(
|
||||
<HomePageHeader
|
||||
hasProject={!!project}
|
||||
onOpenLanguageDialog={onOpenLanguageDialog}
|
||||
onOpenProfile={onOpenProfile}
|
||||
onOpenProjectManager={onOpenProjectManager}
|
||||
/>
|
||||
);
|
||||
},
|
||||
[setToolbar, onOpenLanguageDialog, onOpenProfile, onOpenProjectManager]
|
||||
[
|
||||
setToolbar,
|
||||
onOpenLanguageDialog,
|
||||
onOpenProfile,
|
||||
onOpenProjectManager,
|
||||
project,
|
||||
]
|
||||
);
|
||||
|
||||
const forceUpdateEditor = React.useCallback(() => {
|
||||
|
@@ -1,6 +1,10 @@
|
||||
// @flow
|
||||
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 => {
|
||||
if (isMobile()) {
|
||||
return ''; // Do not display accelerators on mobile devices
|
||||
@@ -13,13 +17,23 @@ export const adaptAcceleratorString = (accelerator: string): string => {
|
||||
.replace(/Alt\+/, '⌥')
|
||||
.replace(/Option\+/, '⌥')
|
||||
.replace(/Delete/, '⌦')
|
||||
.replace(/Backspace/, '⌫');
|
||||
.replace(/Backspace/, '⌫')
|
||||
.replace(/numadd/, '+')
|
||||
.replace(/numsub/, '-')
|
||||
.replace(/num1/, '1')
|
||||
.replace(/num2/, '2')
|
||||
.replace(/num3/, '3');
|
||||
} else {
|
||||
return accelerator
|
||||
.replace(/CmdOrCtrl\+/, 'Ctrl+')
|
||||
.replace(/CommandOrControl\+/, 'Ctrl+')
|
||||
.replace(/Super\+/, 'Win+')
|
||||
.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 MenuItem from '@material-ui/core/MenuItem';
|
||||
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 Divider from '@material-ui/core/Divider';
|
||||
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 = {
|
||||
menuItemWithSubMenu: { justifyContent: 'space-between' },
|
||||
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 [menuOpen, setMenuOpen] = useState(false);
|
||||
const anchorElement = useRef(null);
|
||||
const setAnchorElement = useCallback(element => {
|
||||
anchorElement.current = element;
|
||||
}, []);
|
||||
const popoverStyles = useStyles();
|
||||
const currentlyHovering = React.useRef(false);
|
||||
const [anchorElement, setAnchorElement] = React.useState(null);
|
||||
|
||||
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 (
|
||||
<React.Fragment>
|
||||
@@ -27,27 +83,39 @@ const SubMenuItem = ({ item, buildFromTemplate }) => {
|
||||
dense
|
||||
style={styles.menuItemWithSubMenu}
|
||||
key={item.label}
|
||||
disabled={item.enabled === false}
|
||||
onClick={event => {
|
||||
if (item.enabled === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!anchorElement.current) {
|
||||
setAnchorElement(event.currentTarget);
|
||||
}
|
||||
|
||||
setMenuOpen(!menuOpen);
|
||||
}}
|
||||
disabled={
|
||||
// $FlowFixMe - even if not defined, not a problem.
|
||||
item.enabled === false
|
||||
}
|
||||
onClick={handleOpen}
|
||||
onPointerOver={handleOpen}
|
||||
onPointerLeave={handleLeave}
|
||||
>
|
||||
{item.label}
|
||||
<ArrowRightIcon ref={anchorElement} />
|
||||
<ArrowRightIcon />
|
||||
</MenuItem>
|
||||
<Menu
|
||||
open={menuOpen}
|
||||
anchorEl={anchorElement.current}
|
||||
onClose={() => setMenuOpen(false)}
|
||||
open={!!anchorElement}
|
||||
anchorEl={anchorElement}
|
||||
onClose={handleClose}
|
||||
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)}
|
||||
</Menu>
|
||||
@@ -70,19 +138,19 @@ const SubMenuItem = ({ item, buildFromTemplate }) => {
|
||||
* - submenu
|
||||
*/
|
||||
export default class MaterialUIMenuImplementation {
|
||||
constructor({ onClose }) {
|
||||
_onClose: () => void;
|
||||
constructor({ onClose }: {| onClose: () => void |}) {
|
||||
this._onClose = onClose;
|
||||
}
|
||||
|
||||
buildFromTemplate(template) {
|
||||
buildFromTemplate(template: Array<MenuItemTemplate>) {
|
||||
return template
|
||||
.map((item, id) => {
|
||||
if (item.visible === false) return null;
|
||||
|
||||
// Accelerator is not implemented for Material-UI menus
|
||||
// const accelerator = item.accelerator
|
||||
// ? adaptAcceleratorString(item.accelerator)
|
||||
// : undefined;
|
||||
const accelerator = item.accelerator
|
||||
? adaptAcceleratorString(item.accelerator)
|
||||
: undefined;
|
||||
|
||||
if (item.type === 'separator') {
|
||||
return <Divider key={'separator' + id} style={styles.divider} />;
|
||||
@@ -91,8 +159,14 @@ export default class MaterialUIMenuImplementation {
|
||||
<MenuItem
|
||||
dense
|
||||
key={'checkbox' + item.label}
|
||||
checked={item.checked}
|
||||
disabled={item.enabled === false}
|
||||
checked={
|
||||
// $FlowFixMe - existence should be inferred by Flow.
|
||||
item.checked
|
||||
}
|
||||
disabled={
|
||||
// $FlowFixMe - existence should be inferred by Flow.
|
||||
item.enabled === false
|
||||
}
|
||||
onClick={() => {
|
||||
if (item.enabled === false) {
|
||||
return;
|
||||
@@ -103,6 +177,7 @@ export default class MaterialUIMenuImplementation {
|
||||
}
|
||||
this._onClose();
|
||||
}}
|
||||
style={styles.menuItem}
|
||||
>
|
||||
<ListItemIcon>
|
||||
{item.checked ? <CheckBoxIcon /> : <CheckBoxOutlineBlankIcon />}
|
||||
@@ -134,8 +209,16 @@ export default class MaterialUIMenuImplementation {
|
||||
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>
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user