Compare commits

...

9 Commits

Author SHA1 Message Date
AlexandreSi
4c5f624b34 Improve props name 2023-01-24 15:38:02 +01:00
AlexandreSi
0928247ee1 Improve typing 2023-01-24 15:37:18 +01:00
AlexandreSi
2ae7b1a862 Prettier 2023-01-20 16:26:24 +01:00
AlexandreSi
00e664ef52 Display accelerator in group context menu 2023-01-20 16:24:30 +01:00
AlexandreSi
ed8e272740 Start editing group name when hitting F2 2023-01-20 16:20:10 +01:00
AlexandreSi
84e9373f7e Maintain the active editor in the scene editor 2023-01-20 16:18:44 +01:00
AlexandreSi
d31e816790 Highlight selected group 2023-01-20 16:09:31 +01:00
AlexandreSi
9b26c82597 Add possibility to specify equality test for selected item in virtualized list 2023-01-20 16:07:15 +01:00
AlexandreSi
0054324df8 Add onClick callback to paper and background components 2023-01-20 15:31:51 +01:00
13 changed files with 158 additions and 74 deletions

View File

@@ -237,7 +237,7 @@ const commandsList: { [CommandName]: CommandMetadata } = {
},
RENAME_SCENE_OBJECT: {
area: 'SCENE',
displayText: t`Rename the selected object`,
displayText: t`Rename the selected object or group`,
},
DELETE_INSTANCES: {
area: 'SCENE',

View File

@@ -27,12 +27,13 @@ const styles = {
type Props = {|
...InstancesEditorPropsWithoutSizeAndScroll,
wrappedEditorRef: ?(?InstancesEditor) => void,
onEditorActive?: () => void,
|};
const noop = () => {};
const FullSizeInstancesEditorWithScrollbars = (props: Props) => {
const { wrappedEditorRef, ...otherProps } = props;
const { wrappedEditorRef, onEditorActive, ...otherProps } = props;
const editorRef = React.useRef<?InstancesEditor>(null);
const xScrollbarTrack = React.useRef<?HTMLDivElement>(null);
@@ -361,7 +362,7 @@ const FullSizeInstancesEditorWithScrollbars = (props: Props) => {
return (
<FullSizeMeasurer>
{({ width, height }) => (
<div style={styles.container}>
<div style={styles.container} onClick={onEditorActive}>
{width !== undefined && height !== undefined && (
<InstancesEditor
onViewPositionChanged={

View File

@@ -32,6 +32,7 @@ type Props = {|
unsavedChanges?: ?UnsavedChanges,
i18n: I18nType,
historyHandler?: HistoryHandler,
onEditorActive?: () => void,
|};
export default class InstancePropertiesEditor extends React.Component<Props> {
@@ -238,7 +239,7 @@ export default class InstancePropertiesEditor extends React.Component<Props> {
const { instances } = this.props;
return (
<Background>
<Background onClick={this.props.onEditorActive}>
{!instances || !instances.length
? this._renderEmpty()
: this._renderInstancesProperties()}

View File

@@ -26,6 +26,7 @@ type Props = {|
instances: gdInitialInstancesContainer,
selectedInstances: Array<gdInitialInstance>,
onSelectInstances: (Array<gdInitialInstance>, boolean) => void,
onEditorActive?: () => void,
|};
type RenderedRowInfo = {
@@ -223,7 +224,7 @@ export default class InstancesList extends Component<Props, State> {
return (
<ThemeConsumer>
{muiTheme => (
<div style={styles.container}>
<div style={styles.container} onClick={this.props.onEditorActive}>
<div
style={{ flex: 1 }}
onKeyDown={this._keyboardShortcuts.onKeyDown}

View File

@@ -175,6 +175,7 @@ type Props = {|
// Preview:
hotReloadPreviewButtonProps: HotReloadPreviewButtonProps,
onEditorActive?: () => void,
|};
export type LayersListInterface = {
@@ -234,7 +235,7 @@ const LayersList = React.forwardRef<Props, LayersListInterface>(
const isLightingLayerPresent = hasLightingLayer(props.layersContainer);
return (
<Background>
<Background onClick={props.onEditorActive}>
<ScrollView autoHideScrollbar>
<FullSizeMeasurer>
{({ width }) => (

View File

@@ -25,6 +25,11 @@ import {
serializeToJSObject,
unserializeFromJSObject,
} from '../Utils/Serializer';
import { getShortcutDisplayName } from '../KeyboardShortcuts';
import PreferencesContext, {
type PreferencesValues,
} from '../MainFrame/Preferences/PreferencesContext';
import defaultShortcuts from '../KeyboardShortcuts/DefaultShortcuts';
export const groupWithContextReactDndType = 'GD_GROUP_WITH_CONTEXT';
@@ -61,9 +66,10 @@ type Props = {|
onGroupRenamed?: () => void,
canSetAsGlobalGroup?: boolean,
unsavedChanges?: ?UnsavedChanges,
onEditorActive?: () => void,
|};
export default class GroupsListContainer extends React.Component<Props, State> {
export default class ObjectGroupsList extends React.Component<Props, State> {
static defaultProps = {
onDeleteGroup: (groupWithContext: GroupWithContext, cb: Function) =>
cb(true),
@@ -328,7 +334,7 @@ export default class GroupsListContainer extends React.Component<Props, State> {
if (this.sortableList) this.sortableList.forceUpdateGrid();
};
_renderGroupMenuTemplate = (i18n: I18nType) => (
_renderGroupMenuTemplate = (i18n: I18nType, values: PreferencesValues) => (
groupWithContext: GroupWithContext,
index: number
) => [
@@ -345,6 +351,10 @@ export default class GroupsListContainer extends React.Component<Props, State> {
{
label: i18n._(t`Rename`),
click: () => this._onEditName(groupWithContext),
accelerator: getShortcutDisplayName(
values.userShortcutMap['RENAME_SCENE_OBJECT'] ||
defaultShortcuts.RENAME_SCENE_OBJECT
),
},
{
label: i18n._(t`Set as global group`),
@@ -363,6 +373,12 @@ export default class GroupsListContainer extends React.Component<Props, State> {
},
];
startEditingSelectedGroup = () => {
if (this.state.selectedGroupWithContext) {
this._onEditName(this.state.selectedGroupWithContext);
}
};
render() {
const { globalObjectGroups, objectGroups } = this.props;
const { searchText } = this.state;
@@ -399,59 +415,73 @@ export default class GroupsListContainer extends React.Component<Props, State> {
: null;
return (
<Background>
<div style={styles.listContainer}>
<AutoSizer>
{({ height, width }) => (
<I18n>
{({ i18n }) => (
<SortableVirtualizedItemList
key={listKey}
ref={sortableList => (this.sortableList = sortableList)}
fullList={fullList}
width={width}
height={height}
getItemName={getGroupWithContextName}
getItemId={(groupWithContext, index) => {
return 'group-item-' + index;
}}
isItemBold={isGroupWithContextGlobal}
onEditItem={groupWithContext =>
this.props.onEditGroup(groupWithContext.group)
}
onAddNewItem={this.addGroup}
addNewItemLabel={<Trans>Add a new group</Trans>}
addNewItemId="add-new-group-button"
selectedItems={[]}
onItemSelected={groupWithContext => {
this.setState({
selectedGroupWithContext: groupWithContext,
});
}}
renamedItem={renamedGroupWithContext}
onRename={this._onRename}
buildMenuTemplate={this._renderGroupMenuTemplate(i18n)}
onMoveSelectionToItem={this._moveSelectionTo}
canMoveSelectionToItem={this._canMoveSelectionTo}
reactDndType={groupWithContextReactDndType}
/>
<PreferencesContext.Consumer>
{({ values }) => (
<Background onClick={this.props.onEditorActive}>
<div style={styles.listContainer}>
<AutoSizer>
{({ height, width }) => (
<I18n>
{({ i18n }) => (
<SortableVirtualizedItemList
key={listKey}
ref={sortableList => (this.sortableList = sortableList)}
fullList={fullList}
width={width}
height={height}
getItemName={getGroupWithContextName}
getItemId={(groupWithContext, index) => {
return 'group-item-' + index;
}}
isItemBold={isGroupWithContextGlobal}
onEditItem={groupWithContext =>
this.props.onEditGroup(groupWithContext.group)
}
onAddNewItem={this.addGroup}
addNewItemLabel={<Trans>Add a new group</Trans>}
addNewItemId="add-new-group-button"
selectedItems={
this.state.selectedGroupWithContext
? [this.state.selectedGroupWithContext]
: []
}
onItemSelected={groupWithContext => {
this.setState({
selectedGroupWithContext: groupWithContext,
});
}}
areItemsEqual={(a, b) =>
a.group.ptr === b.group.ptr && a.global === b.global
}
renamedItem={renamedGroupWithContext}
onRename={this._onRename}
buildMenuTemplate={this._renderGroupMenuTemplate(
i18n,
values
)}
onMoveSelectionToItem={this._moveSelectionTo}
canMoveSelectionToItem={this._canMoveSelectionTo}
reactDndType={groupWithContextReactDndType}
/>
)}
</I18n>
)}
</I18n>
)}
</AutoSizer>
</div>
<SearchBar
value={searchText}
onRequestSearch={() => {}}
onChange={text =>
this.setState({
searchText: text,
})
}
aspect="integrated-search-bar"
placeholder={t`Search object groups`}
/>
</Background>
</AutoSizer>
</div>
<SearchBar
value={searchText}
onRequestSearch={() => {}}
onChange={text =>
this.setState({
searchText: text,
})
}
aspect="integrated-search-bar"
placeholder={t`Search object groups`}
/>
</Background>
)}
</PreferencesContext.Consumer>
);
}
}

View File

@@ -137,6 +137,7 @@ type Props = {|
) => string,
unsavedChanges?: ?UnsavedChanges,
hotReloadPreviewButtonProps: HotReloadPreviewButtonProps,
onEditorActive?: () => void,
|};
const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
@@ -169,6 +170,7 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
getThumbnail,
unsavedChanges,
hotReloadPreviewButtonProps,
onEditorActive,
}: Props,
ref
) => {
@@ -798,7 +800,7 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
const screenType = useScreenType();
return (
<Background maxWidth>
<Background maxWidth onClick={onEditorActive}>
<TagChips
tags={selectedObjectTags}
onChange={onChangeSelectedObjectTags}

View File

@@ -41,8 +41,7 @@ type Props = {|
getContextMenuZoomItems: I18nType => Array<MenuItemTemplate>,
setZoomFactor: number => void,
onOpenSettings?: ?() => void,
canRenameObject: boolean,
onRenameObject: () => void,
onRenameObjectOrGroup: () => void,
|};
const Toolbar = (props: Props) => {
@@ -65,8 +64,7 @@ const Toolbar = (props: Props) => {
canDeleteSelection={
props.instancesSelection.getSelectedInstances().length !== 0
}
canRenameObject={props.canRenameObject}
onRenameObject={props.onRenameObject}
onRenameObjectOrGroup={props.onRenameObjectOrGroup}
/>
<ToolbarGroup lastChild>
<IconButton

View File

@@ -16,8 +16,7 @@ type Props = {|
toggleWindowMask: () => void,
toggleGrid: () => void,
setupGrid: () => void,
canRenameObject: boolean,
onRenameObject: () => void,
onRenameObjectOrGroup: () => void,
|};
const ToolbarCommands = (props: Props) => {
@@ -65,8 +64,8 @@ const ToolbarCommands = (props: Props) => {
handler: props.setupGrid,
});
useCommand('RENAME_SCENE_OBJECT', props.canRenameObject, {
handler: props.onRenameObject,
useCommand('RENAME_SCENE_OBJECT', true, {
handler: props.onRenameObjectOrGroup,
});
return null;

View File

@@ -154,6 +154,14 @@ type State = {|
renamedObjectWithContext: ?ObjectWithContext,
selectedObjectsWithContext: Array<ObjectWithContext>,
activeEditor:
| 'instance-properties-editor'
| 'layers-list'
| 'instances-list'
| 'instances-editor'
| 'objects-list'
| 'object-groups-list'
| null,
|};
type CopyCutPasteOptions = {|
@@ -171,6 +179,7 @@ export default class SceneEditor extends React.Component<Props, State> {
contextMenu: ?ContextMenuInterface;
editorMosaic: ?EditorMosaic;
_objectsList: ?ObjectsListInterface;
_objectGroupsList: ?ObjectGroupsList;
_layersList: ?LayersListInterface;
_propertiesEditor: ?InstancePropertiesEditor;
_instancesList: ?InstancesList;
@@ -217,6 +226,8 @@ export default class SceneEditor extends React.Component<Props, State> {
renamedObjectWithContext: null,
selectedObjectsWithContext: [],
activeEditor: null,
};
}
@@ -254,8 +265,7 @@ export default class SceneEditor extends React.Component<Props, State> {
undo={this.undo}
redo={this.redo}
onOpenSettings={this.openSceneProperties}
canRenameObject={this.state.selectedObjectsWithContext.length === 1}
onRenameObject={this._startRenamingSelectedObject}
onRenameObjectOrGroup={this._startRenamingSelectedObjectOrGroup}
/>
);
}
@@ -718,8 +728,15 @@ export default class SceneEditor extends React.Component<Props, State> {
);
};
_startRenamingSelectedObject = () => {
this._onRenameObjectStart(this.state.selectedObjectsWithContext[0]);
_startRenamingSelectedObjectOrGroup = () => {
if (this.state.activeEditor === 'objects-list') {
this._onRenameObjectStart(this.state.selectedObjectsWithContext[0]);
} else if (
this.state.activeEditor === 'object-groups-list' &&
this._objectGroupsList
) {
this._objectGroupsList.startEditingSelectedGroup();
}
};
_onRenameLayer = (
@@ -1354,6 +1371,9 @@ export default class SceneEditor extends React.Component<Props, State> {
i18n={i18n}
project={project}
layout={layout}
onEditorActive={() =>
this.setState({ activeEditor: 'instance-properties-editor' })
}
instances={selectedInstances}
editInstanceVariables={this.editInstanceVariables}
onEditObjectByName={this.editObjectByName}
@@ -1388,6 +1408,9 @@ export default class SceneEditor extends React.Component<Props, State> {
renderEditor: () => (
<LayersList
project={project}
onEditorActive={() =>
this.setState({ activeEditor: 'layers-list' })
}
onEditLayerEffects={this.editLayerEffects}
onEditLayer={this.editLayer}
onRemoveLayer={this._onRemoveLayer}
@@ -1405,6 +1428,9 @@ export default class SceneEditor extends React.Component<Props, State> {
title: t`Instances List`,
renderEditor: () => (
<InstancesList
onEditorActive={() =>
this.setState({ activeEditor: 'instances-list' })
}
instances={initialInstances}
selectedInstances={selectedInstances}
onSelectInstances={this._onSelectInstances}
@@ -1419,6 +1445,9 @@ export default class SceneEditor extends React.Component<Props, State> {
<FullSizeInstancesEditorWithScrollbars
project={project}
layout={layout}
onEditorActive={() =>
this.setState({ activeEditor: 'instances-editor' })
}
initialInstances={initialInstances}
instancesEditorSettings={this.state.instancesEditorSettings}
onChangeInstancesEditorSettings={this.setInstancesEditorSettings}
@@ -1474,6 +1503,9 @@ export default class SceneEditor extends React.Component<Props, State> {
ObjectsRenderingService
)}
project={project}
onEditorActive={() =>
this.setState({ activeEditor: 'objects-list' })
}
objectsContainer={layout}
layout={layout}
onSelectAllInstancesOfObjectInLayout={
@@ -1522,6 +1554,9 @@ export default class SceneEditor extends React.Component<Props, State> {
<I18n>
{({ i18n }) => (
<ObjectGroupsList
onEditorActive={() =>
this.setState({ activeEditor: 'object-groups-list' })
}
globalObjectGroups={project.getObjectGroups()}
objectGroups={layout.getObjectGroups()}
onEditGroup={this.editGroup}
@@ -1531,6 +1566,9 @@ export default class SceneEditor extends React.Component<Props, State> {
this._canObjectOrGroupUseNewName(newName, i18n)
}
unsavedChanges={this.props.unsavedChanges}
ref={objectGroupsList =>
(this._objectGroupsList = objectGroupsList)
}
/>
)}
</I18n>

View File

@@ -19,6 +19,7 @@ type Props = {|
/** Sometimes required on Safari */
noFullHeight?: boolean,
noExpand?: boolean,
onClick?: () => void,
|};
/**
@@ -36,6 +37,7 @@ const Background = (props: Props) => (
...(props.maxWidth ? styles.maxWidth : undefined),
}}
background="dark"
onClick={props.onClick}
>
{props.children}
</Paper>

View File

@@ -13,6 +13,7 @@ type Props = {|
background: 'light' | 'medium' | 'dark',
style?: Object,
square?: boolean,
onClick?: () => void,
|};
const Paper = ({
@@ -22,6 +23,7 @@ const Paper = ({
variant,
style,
square,
onClick,
}: Props) => {
const gdevelopTheme = React.useContext(GDevelopThemeContext);
const backgroundColor =
@@ -39,6 +41,7 @@ const Paper = ({
...style,
}}
square={!!square}
onClick={onClick}
>
{children}
</MuiPaper>

View File

@@ -27,6 +27,7 @@ type Props<Item> = {|
getItemData?: (Item, index: number) => HTMLDataset,
isItemBold?: Item => boolean,
onItemSelected: (?Item) => void,
areItemsEqual?: (Item, Item) => boolean,
onEditItem?: Item => void,
renamedItem: ?Item,
erroredItems?: { [string]: '' | 'error' | 'warning' },
@@ -67,6 +68,7 @@ export default class SortableVirtualizedItemList<Item> extends React.Component<
getItemData,
renderItemLabel,
scaleUpItemIconWhenSelected,
areItemsEqual,
} = this.props;
const nameBeingEdited = renamedItem === item;
@@ -87,7 +89,13 @@ export default class SortableVirtualizedItemList<Item> extends React.Component<
getThumbnail={
getItemThumbnail ? () => getItemThumbnail(item) : undefined
}
selected={selectedItems.indexOf(item) !== -1}
selected={
areItemsEqual
? selectedItems.some(selectedItem =>
areItemsEqual(selectedItem, item)
)
: selectedItems.indexOf(item) !== -1
}
onItemSelected={this.props.onItemSelected}
errorStatus={erroredItems ? erroredItems[itemName] || '' : ''}
buildMenuTemplate={() => this.props.buildMenuTemplate(item, index)}