Editor selection refactor pt1

This commit is contained in:
David Wilson
2023-12-14 18:23:04 +10:00
committed by Gustas
parent b58c1ea5bc
commit 2ced4abc24
41 changed files with 1560 additions and 854 deletions

View File

@@ -11,7 +11,6 @@
using System;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets
@@ -26,29 +25,20 @@ namespace OpenRA.Mods.Common.Widgets
readonly Lazy<TooltipContainerWidget> tooltipContainer;
readonly WorldRenderer worldRenderer;
readonly EditorActionManager editorActionManager;
bool enableTooltips;
[ObjectCreator.UseCtor]
public EditorViewportControllerWidget(World world, WorldRenderer worldRenderer)
public EditorViewportControllerWidget(WorldRenderer worldRenderer)
{
this.worldRenderer = worldRenderer;
tooltipContainer = Exts.Lazy(() => Ui.Root.Get<TooltipContainerWidget>(TooltipContainer));
CurrentBrush = DefaultBrush = new EditorDefaultBrush(this, worldRenderer);
editorActionManager = world.WorldActor.Trait<EditorActionManager>();
editorActionManager.OnChange += EditorActionManagerOnChange;
// Allow zooming out to full map size
worldRenderer.Viewport.UnlockMinimumZoom(0.25f);
}
void EditorActionManagerOnChange()
{
DefaultBrush.SelectedActor = null;
}
public void ClearBrush() { SetBrush(null); }
public void SetBrush(IEditorBrush brush)
{
@@ -106,11 +96,5 @@ namespace OpenRA.Mods.Common.Widgets
cachedViewportPosition = worldRenderer.Viewport.CenterPosition;
CurrentBrush.Tick();
}
public override void Removed()
{
base.Removed();
editorActionManager.OnChange -= EditorActionManagerOnChange;
}
}
}

View File

@@ -38,7 +38,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
readonly EditorActorLayer editorActorLayer;
readonly EditorActionManager editorActionManager;
readonly EditorViewportControllerWidget editor;
readonly BackgroundWidget actorEditPanel;
readonly ContainerWidget actorEditPanel;
readonly LabelWidget typeLabel;
readonly TextFieldWidget actorIDField;
readonly HashSet<TextFieldWidget> typableFields = new();
@@ -51,12 +51,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
readonly Widget sliderOptionTemplate;
readonly Widget dropdownOptionTemplate;
readonly int editPanelPadding; // Padding between right edge of actor and the edit panel.
readonly long scrollVisibleTimeout = 100; // Delay after scrolling map before edit widget becomes visible again.
long lastScrollTime = 0;
int2 lastScrollPosition = int2.Zero;
ActorIDStatus actorIDStatus = ActorIDStatus.Normal;
ActorIDStatus nextActorIDStatus = ActorIDStatus.Normal;
string initialActorID;
@@ -93,8 +87,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic
editorActorLayer = world.WorldActor.Trait<EditorActorLayer>();
editorActionManager = world.WorldActor.Trait<EditorActionManager>();
editor = widget.Parent.Get<EditorViewportControllerWidget>("MAP_EDITOR");
actorEditPanel = editor.Get<BackgroundWidget>("ACTOR_EDIT_PANEL");
editor = widget.Parent.Parent.Get<EditorViewportControllerWidget>("MAP_EDITOR");
var selectTabContainer = widget.Parent.Parent.Get<ContainerWidget>("SELECT_WIDGETS");
actorEditPanel = selectTabContainer.Get<ContainerWidget>("ACTOR_EDIT_PANEL");
typeLabel = actorEditPanel.Get<LabelWidget>("ACTOR_TYPE_LABEL");
actorIDField = actorEditPanel.Get<TextFieldWidget>("ACTOR_ID");
@@ -118,16 +114,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
? TranslationProvider.GetString(DuplicateActorId)
: TranslationProvider.GetString(EnterActorId);
if (logicArgs.TryGetValue("EditPanelPadding", out var yaml))
editPanelPadding = FieldLoader.GetValue<int>("EditPanelPadding", yaml.Value);
okButton.IsDisabled = () => !IsValid() || !editActorPreview.IsDirty;
okButton.IsDisabled = () => !IsValid() || editActorPreview == null || !editActorPreview.IsDirty;
okButton.OnClick = Save;
cancelButton.OnClick = Cancel;
deleteButton.OnClick = Delete;
actorEditPanel.IsVisible = () => CurrentActor != null
&& editor.CurrentBrush == editor.DefaultBrush
&& Game.RunTime > lastScrollTime + scrollVisibleTimeout;
&& editor.CurrentBrush == editor.DefaultBrush;
actorIDField.OnEscKey = _ => actorIDField.YieldKeyboardFocus();
@@ -182,7 +174,6 @@ namespace OpenRA.Mods.Common.Widgets.Logic
if (nextActorIDStatus == ActorIDStatus.Normal)
offset *= -1;
actorEditPanel.Bounds.Height += offset;
initContainer.Bounds.Y += offset;
buttonContainer.Bounds.Y += offset;
}
@@ -190,22 +181,12 @@ namespace OpenRA.Mods.Common.Widgets.Logic
actorIDStatus = nextActorIDStatus;
}
var actor = editor.DefaultBrush.SelectedActor;
var actor = editor.DefaultBrush.Selection.Actor;
if (actor != null)
{
var origin = worldRenderer.Viewport.WorldToViewPx(new int2(actor.Bounds.Right, actor.Bounds.Top));
// If we scrolled, hide the edit box for a moment
if (lastScrollPosition.X != origin.X || lastScrollPosition.Y != origin.Y)
{
lastScrollTime = Game.RunTime;
lastScrollPosition = origin;
}
// If we changed actor, move widgets
// If we changed actor, update details
if (CurrentActor != actor)
{
lastScrollTime = 0; // Ensure visible
CurrentActor = actor;
editActorPreview = new EditActorPreview(CurrentActor);
@@ -365,13 +346,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
}
}
actorEditPanel.Bounds.Height += initContainer.Bounds.Height - oldInitHeight;
buttonContainer.Bounds.Y += initContainer.Bounds.Height - oldInitHeight;
}
// Set the edit panel to the right of the selection border.
actorEditPanel.Bounds.X = origin.X + editPanelPadding;
actorEditPanel.Bounds.Y = origin.Y;
}
else if (CurrentActor != null)
{
@@ -405,7 +381,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
foreach (var f in typableFields)
f.YieldKeyboardFocus();
editor.DefaultBrush.SelectedActor = null;
editor.DefaultBrush.Selection.Actor = null;
CurrentActor = null;
}

View File

@@ -52,7 +52,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
ModData = modData;
World = world;
WorldRenderer = worldRenderer;
Editor = widget.Parent.Get<EditorViewportControllerWidget>("MAP_EDITOR");
Editor = widget.Parent.Parent.Get<EditorViewportControllerWidget>("MAP_EDITOR");
Panel = widget.Get<ScrollPanelWidget>(templateListId);
ItemTemplate = Panel.Get<ScrollItemWidget>(previewTemplateId);
Panel.Layout = new GridLayout(Panel);
@@ -71,6 +71,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic
return true;
};
Editor.DefaultBrush.SelectionChanged += HandleSelectionChanged;
var none = TranslationProvider.GetString(None);
var searchResults = TranslationProvider.GetString(SearchResults);
var all = TranslationProvider.GetString(All);
@@ -103,6 +105,18 @@ namespace OpenRA.Mods.Common.Widgets.Logic
};
}
protected override void Dispose(bool disposing)
{
Editor.DefaultBrush.SelectionChanged -= HandleSelectionChanged;
base.Dispose(disposing);
}
void HandleSelectionChanged()
{
SearchTextField.YieldKeyboardFocus();
}
protected Widget CreateCategoriesPanel(ScrollPanelWidget panel)
{
var categoriesPanel = Ui.LoadWidget("CATEGORY_FILTER_PANEL", null, new WidgetArgs());

View File

@@ -28,7 +28,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic
public LayerSelectorLogic(Widget widget, WorldRenderer worldRenderer)
{
this.worldRenderer = worldRenderer;
editor = widget.Parent.Get<EditorViewportControllerWidget>("MAP_EDITOR");
editor = widget.Parent.Parent.Get<EditorViewportControllerWidget>("MAP_EDITOR");
editorCursor = worldRenderer.World.WorldActor.Trait<EditorCursorLayer>();
layerTemplateList = widget.Get<ScrollPanelWidget>("LAYERTEMPLATE_LIST");

View File

@@ -18,29 +18,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic
{
public class MapEditorLogic : ChromeLogic
{
MapCopyFilters copyFilters = MapCopyFilters.All;
[ObjectCreator.UseCtor]
public MapEditorLogic(Widget widget, World world, WorldRenderer worldRenderer)
{
var editorViewport = widget.Get<EditorViewportControllerWidget>("MAP_EDITOR");
var copypasteButton = widget.GetOrNull<ButtonWidget>("COPYPASTE_BUTTON");
if (copypasteButton != null)
{
var copyPasteKey = copypasteButton.Key.GetValue();
copypasteButton.OnClick = () => editorViewport.SetBrush(new EditorCopyPasteBrush(editorViewport, worldRenderer, () => copyFilters));
copypasteButton.IsHighlighted = () => editorViewport.CurrentBrush is EditorCopyPasteBrush;
}
var copyFilterDropdown = widget.Get<DropDownButtonWidget>("COPYFILTER_BUTTON");
copyFilterDropdown.OnMouseDown = _ =>
{
copyFilterDropdown.RemovePanel();
copyFilterDropdown.AttachPanel(CreateCategoriesPanel());
};
var coordinateLabel = widget.GetOrNull<LabelWidget>("COORDINATE_LABEL");
if (coordinateLabel != null)
{
@@ -71,25 +53,5 @@ namespace OpenRA.Mods.Common.Widgets.Logic
redoButton.OnClick = () => actionManager.Redo();
}
}
Widget CreateCategoriesPanel()
{
var categoriesPanel = Ui.LoadWidget("COPY_FILTER_PANEL", null, new WidgetArgs());
var categoryTemplate = categoriesPanel.Get<CheckboxWidget>("CATEGORY_TEMPLATE");
MapCopyFilters[] allCategories = { MapCopyFilters.Terrain, MapCopyFilters.Resources, MapCopyFilters.Actors };
foreach (var cat in allCategories)
{
var category = (CheckboxWidget)categoryTemplate.Clone();
category.GetText = () => cat.ToString();
category.IsChecked = () => copyFilters.HasFlag(cat);
category.IsVisible = () => true;
category.OnClick = () => copyFilters ^= cat;
categoriesPanel.AddChild(category);
}
return categoriesPanel;
}
}
}

View File

@@ -0,0 +1,128 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* This file is part of OpenRA, which is free software. It is made
* available to you under the terms of the GNU General Public License
* as published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version. For more
* information, see COPYING.
*/
#endregion
using System.Collections.Generic;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.EditorBrushes;
using OpenRA.Mods.Common.Traits;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets.Logic
{
public class MapEditorSelectionLogic : ChromeLogic
{
readonly EditorViewportControllerWidget editor;
readonly WorldRenderer worldRenderer;
readonly ContainerWidget actorEditPanel;
readonly ContainerWidget areaEditPanel;
readonly CheckboxWidget copyTerrainCheckbox;
readonly CheckboxWidget copyResourcesCheckbox;
readonly CheckboxWidget copyActorsCheckbox;
readonly EditorActorLayer editorActorLayer;
MapCopyFilters copyFilters = MapCopyFilters.All;
EditorClipboard? clipboard;
readonly IResourceLayer resourceLayer;
[ObjectCreator.UseCtor]
public MapEditorSelectionLogic(Widget widget, World world, WorldRenderer worldRenderer)
{
this.worldRenderer = worldRenderer;
editorActorLayer = world.WorldActor.Trait<EditorActorLayer>();
resourceLayer = world.WorldActor.Trait<IResourceLayer>();
editor = widget.Get<EditorViewportControllerWidget>("MAP_EDITOR");
var selectTabContainer = widget.Get("SELECT_WIDGETS");
actorEditPanel = selectTabContainer.Get<ContainerWidget>("ACTOR_EDIT_PANEL");
areaEditPanel = selectTabContainer.Get<ContainerWidget>("AREA_EDIT_PANEL");
actorEditPanel.IsVisible = () => editor.CurrentBrush == editor.DefaultBrush && editor.DefaultBrush.Selection.Actor != null;
areaEditPanel.IsVisible = () => !actorEditPanel.IsVisible();
copyTerrainCheckbox = areaEditPanel.Get<CheckboxWidget>("COPY_FILTER_TERRAIN_CHECKBOX");
copyResourcesCheckbox = areaEditPanel.Get<CheckboxWidget>("COPY_FILTER_RESOURCES_CHECKBOX");
copyActorsCheckbox = areaEditPanel.Get<CheckboxWidget>("COPY_FILTER_ACTORS_CHECKBOX");
copyTerrainCheckbox.IsDisabled = () => editor.CurrentBrush is EditorCopyPasteBrush;
copyResourcesCheckbox.IsDisabled = () => editor.CurrentBrush is EditorCopyPasteBrush;
copyActorsCheckbox.IsDisabled = () => editor.CurrentBrush is EditorCopyPasteBrush;
var copyButton = widget.Get<ButtonWidget>("COPY_BUTTON");
copyButton.OnClick = () => clipboard = CopySelectionContents();
copyButton.IsDisabled = () => editor.DefaultBrush.Selection.Area == null;
var pasteButton = widget.Get<ButtonWidget>("PASTE_BUTTON");
pasteButton.OnClick = () =>
{
if (clipboard == null)
return;
editor.SetBrush(new EditorCopyPasteBrush(
editor,
worldRenderer,
clipboard.Value,
resourceLayer,
() => copyFilters));
};
pasteButton.IsDisabled = () => clipboard == null;
pasteButton.IsHighlighted = () => editor.CurrentBrush is EditorCopyPasteBrush;
var closeAreaSelectionButton = areaEditPanel.Get<ButtonWidget>("SELECTION_CANCEL_BUTTON");
closeAreaSelectionButton.OnClick = () => editor.DefaultBrush.ClearSelection();
CreateCategoryPanel(MapCopyFilters.Terrain, copyTerrainCheckbox);
CreateCategoryPanel(MapCopyFilters.Resources, copyResourcesCheckbox);
CreateCategoryPanel(MapCopyFilters.Actors, copyActorsCheckbox);
}
EditorClipboard CopySelectionContents()
{
var selection = editor.DefaultBrush.Selection.Area;
var source = new CellCoordsRegion(selection.TopLeft, selection.BottomRight);
var mapTiles = worldRenderer.World.Map.Tiles;
var mapHeight = worldRenderer.World.Map.Height;
var mapResources = worldRenderer.World.Map.Resources;
var previews = new Dictionary<string, EditorActorPreview>();
var tiles = new Dictionary<CPos, ClipboardTile>();
foreach (var cell in source)
{
if (!mapTiles.Contains(cell))
continue;
tiles.Add(cell, new ClipboardTile(mapTiles[cell], mapResources[cell], resourceLayer.GetResource(cell), mapHeight[cell]));
if (copyFilters.HasFlag(MapCopyFilters.Actors))
foreach (var preview in selection.SelectMany(editorActorLayer.PreviewsAt).Distinct())
previews.TryAdd(preview.ID, preview);
}
return new EditorClipboard(selection, previews, tiles);
}
void CreateCategoryPanel(MapCopyFilters copyFilter, CheckboxWidget checkbox)
{
checkbox.GetText = () => copyFilter.ToString();
checkbox.IsChecked = () => copyFilters.HasFlag(copyFilter);
checkbox.IsVisible = () => true;
checkbox.OnClick = () => copyFilters ^= copyFilter;
}
}
}

View File

@@ -16,31 +16,74 @@ namespace OpenRA.Mods.Common.Widgets.Logic
public class MapEditorTabsLogic : ChromeLogic
{
readonly Widget widget;
readonly EditorViewportControllerWidget editor;
protected enum MenuType { Tiles, Layers, Actors, History }
protected enum MenuType { Select, Tiles, Layers, Actors, Tools, History }
protected MenuType menuType = MenuType.Tiles;
readonly Widget tabContainer;
MenuType lastSelectedTab = MenuType.Tiles;
[ObjectCreator.UseCtor]
public MapEditorTabsLogic(Widget widget)
{
this.widget = widget;
editor = widget.Parent.Parent.Get<EditorViewportControllerWidget>("MAP_EDITOR");
editor.DefaultBrush.SelectionChanged += HandleSelectionChanged;
tabContainer = widget.Get("MAP_EDITOR_TAB_CONTAINER");
SetupTab(null, "SELECT_WIDGETS", MenuType.Select);
SetupTab("TILES_TAB", "TILE_WIDGETS", MenuType.Tiles);
SetupTab("OVERLAYS_TAB", "LAYER_WIDGETS", MenuType.Layers);
SetupTab("ACTORS_TAB", "ACTOR_WIDGETS", MenuType.Actors);
SetupTab("TOOLS_TAB", "TOOLS_WIDGETS", MenuType.Tools);
SetupTab("HISTORY_TAB", "HISTORY_WIDGETS", MenuType.History);
}
protected override void Dispose(bool disposing)
{
editor.DefaultBrush.SelectionChanged -= HandleSelectionChanged;
base.Dispose(disposing);
}
void SetupTab(string buttonId, string tabId, MenuType tabType)
{
var tab = tabContainer.Get<ButtonWidget>(buttonId);
tab.IsHighlighted = () => menuType == tabType;
tab.OnClick = () => menuType = tabType;
if (buttonId != null)
{
var tab = tabContainer.Get<ButtonWidget>(buttonId);
tab.IsHighlighted = () => menuType == tabType;
tab.OnClick = () => menuType = SelectTab(tabType);
}
var container = widget.Parent.Get<ContainerWidget>(tabId);
container.IsVisible = () => menuType == tabType;
}
MenuType SelectTab(MenuType newMenuType)
{
if (menuType == MenuType.Select)
{
editor.SetBrush(editor.DefaultBrush);
editor.DefaultBrush.ClearSelection();
}
if (newMenuType != MenuType.Select)
lastSelectedTab = newMenuType;
return newMenuType;
}
void HandleSelectionChanged()
{
var actor = editor.DefaultBrush.Selection.Actor;
var area = editor.DefaultBrush.Selection.Area;
if (menuType != MenuType.Select && (actor != null || area != null))
menuType = MenuType.Select;
else if (menuType == MenuType.Select && actor == null && area == null)
menuType = lastSelectedTab;
}
}
}

View File

@@ -30,10 +30,13 @@ namespace OpenRA.Mods.Common.Widgets.Logic
readonly TerrainGeometryOverlay terrainGeometryTrait;
readonly BuildableTerrainOverlay buildableTerrainTrait;
readonly Widget widget;
[ObjectCreator.UseCtor]
public MapOverlaysLogic(Widget widget, World world, ModData modData, Dictionary<string, MiniYaml> logicArgs)
{
this.widget = widget;
terrainGeometryTrait = world.WorldActor.Trait<TerrainGeometryOverlay>();
buildableTerrainTrait = world.WorldActor.Trait<BuildableTerrainOverlay>();
@@ -81,28 +84,23 @@ namespace OpenRA.Mods.Common.Widgets.Logic
Widget CreateOverlaysPanel()
{
var categoriesPanel = Ui.LoadWidget("OVERLAY_PANEL", null, new WidgetArgs());
var categoryTemplate = categoriesPanel.Get<CheckboxWidget>("CATEGORY_TEMPLATE");
var categoriesPanel = widget.Get<Widget>("TOOLS_WIDGETS");
var showGridCheckbox = categoriesPanel.Get<CheckboxWidget>("SHOW_TILE_GRID");
var showBuildableAreaCheckbox = categoriesPanel.Get<CheckboxWidget>("SHOW_BUILDABLE_AREA");
MapOverlays[] allCategories = { MapOverlays.Grid, MapOverlays.Buildable };
foreach (var cat in allCategories)
{
var category = (CheckboxWidget)categoryTemplate.Clone();
category.GetText = () => cat.ToString();
category.IsVisible = () => true;
if (cat.HasFlag(MapOverlays.Grid))
{
category.IsChecked = () => terrainGeometryTrait.Enabled;
category.OnClick = () => terrainGeometryTrait.Enabled ^= true;
showGridCheckbox.IsChecked = () => terrainGeometryTrait.Enabled;
showGridCheckbox.OnClick = () => terrainGeometryTrait.Enabled ^= true;
}
else if (cat.HasFlag(MapOverlays.Buildable))
{
category.IsChecked = () => buildableTerrainTrait.Enabled;
category.OnClick = () => buildableTerrainTrait.Enabled ^= true;
showBuildableAreaCheckbox.IsChecked = () => buildableTerrainTrait.Enabled;
showBuildableAreaCheckbox.OnClick = () => buildableTerrainTrait.Enabled ^= true;
}
categoriesPanel.AddChild(category);
}
return categoriesPanel;