Merge remote-tracking branch 'origin/master' into sdl_platform

This commit is contained in:
2025-10-14 01:08:57 +03:00
292 changed files with 4020 additions and 1770 deletions

View File

@@ -673,6 +673,7 @@ namespace FlaxEditor.Modules
pasteAction.Do(out _, out var nodeParents);
// Select spawned objects (parents only)
newSelection.Clear();
newSelection.AddRange(nodeParents);
var selectAction = new SelectionChangeAction(Selection.ToArray(), newSelection.ToArray(), OnSelectionUndo);
selectAction.Do();

View File

@@ -54,6 +54,9 @@ namespace FlaxEditor.Modules.SourceCodeEditing
case CodeEditorTypes.VS2022:
Name = "Visual Studio 2022";
break;
case CodeEditorTypes.VS2026:
Name = "Visual Studio 2026";
break;
case CodeEditorTypes.VSCode:
Name = "Visual Studio Code";
break;
@@ -110,6 +113,7 @@ namespace FlaxEditor.Modules.SourceCodeEditing
case CodeEditorTypes.VS2017:
case CodeEditorTypes.VS2019:
case CodeEditorTypes.VS2022:
case CodeEditorTypes.VS2026:
// TODO: finish dynamic files adding to the project
//Editor.Instance.ProgressReporting.GenerateScriptsProjectFiles.RunAsync();
break;

View File

@@ -70,6 +70,53 @@ namespace FlaxEditor.Modules
private bool _progressFailed;
ContextMenuSingleSelectGroup<int> _numberOfClientsGroup = new ContextMenuSingleSelectGroup<int>();
/// <summary>
/// Defines a viewport scaling option.
/// </summary>
public class ViewportScaleOption
{
/// <summary>
/// Defines the viewport scale type.
/// </summary>
public enum ViewportScaleType
{
/// <summary>
/// Resolution.
/// </summary>
Resolution = 0,
/// <summary>
/// Aspect Ratio.
/// </summary>
Aspect = 1,
}
/// <summary>
/// The name.
/// </summary>
public string Label;
/// <summary>
/// The Type of scaling to do.
/// </summary>
public ViewportScaleType ScaleType;
/// <summary>
/// The width and height to scale by.
/// </summary>
public Int2 Size;
}
/// <summary>
/// The default viewport scaling options.
/// </summary>
public List<ViewportScaleOption> DefaultViewportScaleOptions = new List<ViewportScaleOption>();
/// <summary>
/// The user defined viewport scaling options.
/// </summary>
public List<ViewportScaleOption> CustomViewportScaleOptions = new List<ViewportScaleOption>();
private ContextMenuButton _menuFileSaveScenes;
private ContextMenuButton _menuFileReloadScenes;
@@ -409,6 +456,8 @@ namespace FlaxEditor.Modules
// Update window background
mainWindow.BackgroundColor = Style.Current.Background;
InitViewportScaleOptions();
InitSharedMenus();
InitMainMenu(mainWindow);
@@ -422,6 +471,57 @@ namespace FlaxEditor.Modules
mainWindow.PerformLayout(true);
}
private void InitViewportScaleOptions()
{
if (DefaultViewportScaleOptions.Count == 0)
{
DefaultViewportScaleOptions.Add(new ViewportScaleOption
{
Label = "Free Aspect",
ScaleType = ViewportScaleOption.ViewportScaleType.Aspect,
Size = new Int2(1, 1),
});
DefaultViewportScaleOptions.Add(new ViewportScaleOption
{
Label = "16:9 Aspect",
ScaleType = ViewportScaleOption.ViewportScaleType.Aspect,
Size = new Int2(16, 9),
});
DefaultViewportScaleOptions.Add(new ViewportScaleOption
{
Label = "16:10 Aspect",
ScaleType = ViewportScaleOption.ViewportScaleType.Aspect,
Size = new Int2(16, 10),
});
DefaultViewportScaleOptions.Add(new ViewportScaleOption
{
Label = "1920x1080 Resolution (Full HD)",
ScaleType = ViewportScaleOption.ViewportScaleType.Resolution,
Size = new Int2(1920, 1080),
});
DefaultViewportScaleOptions.Add(new ViewportScaleOption
{
Label = "2560x1440 Resolution (2K)",
ScaleType = ViewportScaleOption.ViewportScaleType.Resolution,
Size = new Int2(2560, 1440),
});
}
if (Editor.Instance.ProjectCache.TryGetCustomData("CustomViewportScalingOptions", out string data))
{
CustomViewportScaleOptions = JsonSerializer.Deserialize<List<ViewportScaleOption>>(data);
}
}
/// <summary>
/// Saves the custom viewport scaling options.
/// </summary>
public void SaveCustomViewportScalingOptions()
{
var customOptions = JsonSerializer.Serialize(CustomViewportScaleOptions);
Editor.Instance.ProjectCache.SetCustomData("CustomViewportScalingOptions", customOptions);
}
/// <inheritdoc />
public override void OnUpdate()
{
@@ -546,6 +646,7 @@ namespace FlaxEditor.Modules
_menuFileGenerateScriptsProjectFiles = cm.AddButton("Generate scripts project files", inputOptions.GenerateScriptsProject, Editor.ProgressReporting.GenerateScriptsProjectFiles.RunAsync);
_menuFileRecompileScripts = cm.AddButton("Recompile scripts", inputOptions.RecompileScripts, ScriptsBuilder.Compile);
cm.AddSeparator();
cm.AddButton("New project", NewProject);
cm.AddButton("Open project...", OpenProject);
cm.AddButton("Reload project", ReloadProject);
cm.AddButton("Open project folder", () => FileSystem.ShowFileExplorer(Editor.Instance.GameProject.ProjectFolderPath));
@@ -576,7 +677,7 @@ namespace FlaxEditor.Modules
if (item != null)
Editor.ContentEditing.Open(item);
});
cm.AddButton("Editor Options", () => Editor.Windows.EditorOptionsWin.Show());
cm.AddButton("Editor Options", inputOptions.EditorOptionsWindow, () => Editor.Windows.EditorOptionsWin.Show());
// Scene
MenuScene = MainMenu.AddButton("Scene");
@@ -851,6 +952,17 @@ namespace FlaxEditor.Modules
MasterPanel.Offsets = new Margin(0, 0, ToolStrip.Bottom, StatusBar.Height);
}
private void NewProject()
{
// Ask user to create project file
if (FileSystem.ShowSaveFileDialog(Editor.Windows.MainWindow, null, "Project files (*.flaxproj)\0*.flaxproj\0All files (*.*)\0*.*\0", false, "Create project file", out var files))
return;
if (files != null && files.Length > 0)
{
Editor.NewProject(files[0]);
}
}
private void OpenProject()
{
// Ask user to select project file
@@ -1108,5 +1220,267 @@ namespace FlaxEditor.Modules
MenuTools = null;
MenuHelp = null;
}
internal void CreateViewportSizingContextMenu(ContextMenu vsMenu, int defaultScaleActiveIndex, int customScaleActiveIndex, bool prefabViewport, Action<ViewportScaleOption> changeView, Action<int, int> changeActiveIndices)
{
// Add default viewport sizing options
var defaultOptions = DefaultViewportScaleOptions;
for (int i = 0; i < defaultOptions.Count; i++)
{
var viewportScale = defaultOptions[i];
if (prefabViewport && viewportScale.ScaleType == ViewportScaleOption.ViewportScaleType.Aspect)
continue; // Skip aspect ratio types in prefab
var button = vsMenu.AddButton(viewportScale.Label);
button.CloseMenuOnClick = false;
button.Tag = viewportScale;
// No default index is active
if (defaultScaleActiveIndex == -1)
{
button.Icon = SpriteHandle.Invalid;
}
// This is the active index
else if (defaultScaleActiveIndex == i)
{
button.Icon = Style.Current.CheckBoxTick;
changeView(viewportScale);
}
button.Clicked += () =>
{
if (button.Tag == null)
return;
// Reset selected icon on all buttons
foreach (var child in vsMenu.Items)
{
if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v)
{
if (cmb == button)
{
button.Icon = Style.Current.CheckBoxTick;
var index = defaultOptions.FindIndex(x => x == v);
changeActiveIndices(index, -1); // Reset custom index because default was chosen
changeView(v);
}
else if (cmb.Icon != SpriteHandle.Invalid)
{
cmb.Icon = SpriteHandle.Invalid;
}
}
}
};
}
if (defaultOptions.Count != 0)
vsMenu.AddSeparator();
// Add custom viewport options
var customOptions = CustomViewportScaleOptions;
for (int i = 0; i < customOptions.Count; i++)
{
var viewportScale = customOptions[i];
if (prefabViewport && viewportScale.ScaleType == ViewportScaleOption.ViewportScaleType.Aspect)
continue; // Skip aspect ratio types in prefab
var childCM = vsMenu.AddChildMenu(viewportScale.Label);
childCM.CloseMenuOnClick = false;
childCM.Tag = viewportScale;
// No custom index is active
if (customScaleActiveIndex == -1)
{
childCM.Icon = SpriteHandle.Invalid;
}
// This is the active index
else if (customScaleActiveIndex == i)
{
childCM.Icon = Style.Current.CheckBoxTick;
changeView(viewportScale);
}
var applyButton = childCM.ContextMenu.AddButton("Apply");
applyButton.Tag = childCM.Tag = viewportScale;
applyButton.CloseMenuOnClick = false;
applyButton.Clicked += () =>
{
if (childCM.Tag == null)
return;
// Reset selected icon on all buttons
foreach (var child in vsMenu.Items)
{
if (child is ContextMenuButton cmb && cmb.Tag is UIModule.ViewportScaleOption v)
{
if (child == childCM)
{
childCM.Icon = Style.Current.CheckBoxTick;
var index = customOptions.FindIndex(x => x == v);
changeActiveIndices(-1, index); // Reset default index because custom was chosen
changeView(v);
}
else if (cmb.Icon != SpriteHandle.Invalid)
{
cmb.Icon = SpriteHandle.Invalid;
}
}
}
};
var deleteButton = childCM.ContextMenu.AddButton("Delete");
deleteButton.CloseMenuOnClick = false;
deleteButton.Clicked += () =>
{
if (childCM.Tag == null)
return;
var v = (ViewportScaleOption)childCM.Tag;
if (childCM.Icon != SpriteHandle.Invalid)
{
changeActiveIndices(-1, 0);
changeView(defaultOptions[0]);
}
customOptions.Remove(v);
SaveCustomViewportScalingOptions();
vsMenu.DisposeAllItems();
CreateViewportSizingContextMenu(vsMenu, defaultScaleActiveIndex, customScaleActiveIndex, prefabViewport, changeView, changeActiveIndices);
vsMenu.PerformLayout();
};
}
if (customOptions.Count != 0)
vsMenu.AddSeparator();
// Add button
var add = vsMenu.AddButton("Add...");
add.CloseMenuOnClick = false;
add.Clicked += () =>
{
var popup = new ContextMenuBase
{
Size = new Float2(230, 125),
ClipChildren = false,
CullChildren = false,
};
popup.Show(add, new Float2(add.Width, 0));
var nameLabel = new Label
{
Parent = popup,
AnchorPreset = AnchorPresets.TopLeft,
Text = "Name",
HorizontalAlignment = TextAlignment.Near,
};
nameLabel.LocalX += 10;
nameLabel.LocalY += 10;
var nameTextBox = new TextBox
{
Parent = popup,
AnchorPreset = AnchorPresets.TopLeft,
IsMultiline = false,
};
nameTextBox.LocalX += 100;
nameTextBox.LocalY += 10;
var typeLabel = new Label
{
Parent = popup,
AnchorPreset = AnchorPresets.TopLeft,
Text = "Type",
HorizontalAlignment = TextAlignment.Near,
};
typeLabel.LocalX += 10;
typeLabel.LocalY += 35;
var typeDropdown = new Dropdown
{
Parent = popup,
AnchorPreset = AnchorPresets.TopLeft,
Items = { "Aspect", "Resolution" },
SelectedItem = "Aspect",
Visible = !prefabViewport,
Width = nameTextBox.Width
};
typeDropdown.LocalY += 35;
typeDropdown.LocalX += 100;
var whLabel = new Label
{
Parent = popup,
AnchorPreset = AnchorPresets.TopLeft,
Text = "Width & Height",
HorizontalAlignment = TextAlignment.Near,
};
whLabel.LocalX += 10;
whLabel.LocalY += 60;
var wValue = new IntValueBox(16)
{
Parent = popup,
AnchorPreset = AnchorPresets.TopLeft,
MinValue = 1,
Width = 55,
};
wValue.LocalY += 60;
wValue.LocalX += 100;
var hValue = new IntValueBox(9)
{
Parent = popup,
AnchorPreset = AnchorPresets.TopLeft,
MinValue = 1,
Width = 55,
};
hValue.LocalY += 60;
hValue.LocalX += 165;
var submitButton = new Button
{
Parent = popup,
AnchorPreset = AnchorPresets.TopLeft,
Text = "Submit",
Width = 70,
};
submitButton.LocalX += 40;
submitButton.LocalY += 90;
submitButton.Clicked += () =>
{
Enum.TryParse(typeDropdown.SelectedItem, out ViewportScaleOption.ViewportScaleType type);
if (prefabViewport)
type = ViewportScaleOption.ViewportScaleType.Resolution;
var combineString = type == ViewportScaleOption.ViewportScaleType.Aspect ? ":" : "x";
var name = nameTextBox.Text + " (" + wValue.Value + combineString + hValue.Value + ") " + typeDropdown.SelectedItem;
var newViewportOption = new ViewportScaleOption
{
ScaleType = type,
Label = name,
Size = new Int2(wValue.Value, hValue.Value),
};
customOptions.Add(newViewportOption);
SaveCustomViewportScalingOptions();
vsMenu.DisposeAllItems();
CreateViewportSizingContextMenu(vsMenu, defaultScaleActiveIndex, customScaleActiveIndex, prefabViewport, changeView, changeActiveIndices);
vsMenu.PerformLayout();
};
var cancelButton = new Button
{
Parent = popup,
AnchorPreset = AnchorPresets.TopLeft,
Text = "Cancel",
Width = 70,
};
cancelButton.LocalX += 120;
cancelButton.LocalY += 90;
cancelButton.Clicked += () =>
{
nameTextBox.Clear();
typeDropdown.SelectedItem = "Aspect";
hValue.Value = 9;
wValue.Value = 16;
popup.Hide();
};
};
}
}
}