#region Copyright & License Information /* * Copyright 2007-2018 The OpenRA Developers (see AUTHORS) * 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; using System.Collections.Generic; using System.Linq; using OpenRA.Graphics; using OpenRA.Mods.Common.Traits; using OpenRA.Primitives; using OpenRA.Widgets; namespace OpenRA.Mods.Common.Widgets.Logic { public class ActorSelectorLogic : CommonSelectorLogic { class ActorSelectorActor { public readonly ActorInfo Actor; public readonly string[] Categories; public readonly string[] SearchTerms; public readonly string Tooltip; public ActorSelectorActor(ActorInfo actor, string[] categories, string[] searchTerms, string tooltip) { Actor = actor; Categories = categories; SearchTerms = searchTerms; Tooltip = tooltip; } } readonly DropDownButtonWidget ownersDropDown; readonly ScrollPanelWidget panel; readonly ScrollItemWidget itemTemplate; readonly Ruleset mapRules; readonly ActorSelectorActor[] allActors; PlayerReference selectedOwner; [ObjectCreator.UseCtor] public ActorSelectorLogic(Widget widget, World world, WorldRenderer worldRenderer) : base(widget, world, worldRenderer) { mapRules = world.Map.Rules; ownersDropDown = widget.Get("OWNERS_DROPDOWN"); panel = widget.Get("ACTORTEMPLATE_LIST"); itemTemplate = panel.Get("ACTORPREVIEW_TEMPLATE"); panel.Layout = new GridLayout(panel); var editorLayer = world.WorldActor.Trait(); selectedOwner = editorLayer.Players.Players.Values.First(); Func setupItem = (option, template) => { var item = ScrollItemWidget.Setup(template, () => selectedOwner == option, () => SelectOwner(option)); item.Get("LABEL").GetText = () => option.Name; item.GetColor = () => option.Color.RGB; return item; }; editorLayer.OnPlayerRemoved = () => { if (editorLayer.Players.Players.Values.Any(p => p.Name == selectedOwner.Name)) return; SelectOwner(editorLayer.Players.Players.Values.First()); }; ownersDropDown.OnClick = () => { var owners = editorLayer.Players.Players.Values.OrderBy(p => p.Name); ownersDropDown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 270, owners, setupItem); }; ownersDropDown.Text = selectedOwner.Name; ownersDropDown.TextColor = selectedOwner.Color.RGB; var tileSetId = world.Map.Rules.TileSet.Id; var allActorsTemp = new List(); foreach (var a in mapRules.Actors.Values) { // Partial templates are not allowed if (a.Name.Contains('^')) continue; // Actor must have a preview associated with it if (!a.HasTraitInfo()) continue; var editorData = a.TraitInfoOrDefault(); // Actor must be included in at least one category if (editorData == null || editorData.Categories == null) continue; // Excluded by tileset if (editorData.ExcludeTilesets != null && editorData.ExcludeTilesets.Contains(tileSetId)) continue; if (editorData.RequireTilesets != null && !editorData.RequireTilesets.Contains(tileSetId)) continue; var tooltip = a.TraitInfos().FirstOrDefault(ti => ti.EnabledByDefault) as TooltipInfoBase ?? a.TraitInfos().FirstOrDefault(ti => ti.EnabledByDefault); var searchTerms = new List() { a.Name }; if (tooltip != null) searchTerms.Add(tooltip.Name); var tooltipText = (tooltip == null ? "Type: " : tooltip.Name + "\nType: ") + a.Name; allActorsTemp.Add(new ActorSelectorActor(a, editorData.Categories, searchTerms.ToArray(), tooltipText)); } allActors = allActorsTemp.ToArray(); allCategories = allActors.SelectMany(ac => ac.Categories) .Distinct() .OrderBy(x => x) .ToArray(); foreach (var c in allCategories) { SelectedCategories.Add(c); FilteredCategories.Add(c); } var searchTextField = widget.Get("SEARCH_TEXTFIELD"); searchTextField.OnTextEdited = () => { searchFilter = searchTextField.Text.Trim(); FilteredCategories.Clear(); if (!string.IsNullOrEmpty(searchFilter)) FilteredCategories.AddRange( allActors.Where(t => t.SearchTerms.Any( s => s.IndexOf(searchFilter, StringComparison.OrdinalIgnoreCase) >= 0)) .SelectMany(t => t.Categories) .Distinct() .OrderBy(x => x)); else FilteredCategories.AddRange(allCategories); InitializePreviews(); }; searchTextField.OnEscKey = () => { searchTextField.Text = ""; searchTextField.YieldKeyboardFocus(); return true; }; var actorCategorySelector = widget.Get("CATEGORIES_DROPDOWN"); actorCategorySelector.GetText = () => { if (SelectedCategories.Count == 0) return "None"; if (!string.IsNullOrEmpty(searchFilter)) return "Search Results"; if (SelectedCategories.Count == 1) return SelectedCategories.First(); if (SelectedCategories.Count == allCategories.Length) return "All"; return "Multiple"; }; actorCategorySelector.OnMouseDown = _ => { if (searchTextField != null) searchTextField.YieldKeyboardFocus(); actorCategorySelector.RemovePanel(); actorCategorySelector.AttachPanel(CreateCategoriesPanel(panel)); }; InitializePreviews(); } void SelectOwner(PlayerReference option) { selectedOwner = option; ownersDropDown.Text = option.Name; ownersDropDown.TextColor = option.Color.RGB; InitializePreviews(); } protected override void InitializePreviews() { panel.RemoveChildren(); if (!SelectedCategories.Any()) return; foreach (var a in allActors) { if (!SelectedCategories.Overlaps(a.Categories)) continue; if (!string.IsNullOrEmpty(searchFilter) && !a.SearchTerms.Any(s => s.IndexOf(searchFilter, StringComparison.OrdinalIgnoreCase) >= 0)) continue; var actor = a.Actor; var td = new TypeDictionary(); td.Add(new OwnerInit(selectedOwner.Name)); td.Add(new FactionInit(selectedOwner.Faction)); foreach (var api in actor.TraitInfos()) foreach (var o in api.ActorPreviewInits(actor, ActorPreviewType.MapEditorSidebar)) td.Add(o); try { var item = ScrollItemWidget.Setup(itemTemplate, () => { var brush = Editor.CurrentBrush as EditorActorBrush; return brush != null && brush.Actor == actor; }, () => Editor.SetBrush(new EditorActorBrush(Editor, actor, selectedOwner, WorldRenderer))); var preview = item.Get("ACTOR_PREVIEW"); preview.SetPreview(actor, td); // Scale templates to fit within the panel var scale = 1f; if (scale * preview.IdealPreviewSize.X > itemTemplate.Bounds.Width) scale = (itemTemplate.Bounds.Width - panel.ItemSpacing) / (float)preview.IdealPreviewSize.X; preview.GetScale = () => scale; preview.Bounds.Width = (int)(scale * preview.IdealPreviewSize.X); preview.Bounds.Height = (int)(scale * preview.IdealPreviewSize.Y); item.Bounds.Width = preview.Bounds.Width + 2 * preview.Bounds.X; item.Bounds.Height = preview.Bounds.Height + 2 * preview.Bounds.Y; item.IsVisible = () => true; item.GetTooltipText = () => a.Tooltip; panel.AddChild(item); } catch { Log.Write("debug", "Map editor ignoring actor {0}, because of missing sprites for tileset {1}.", actor.Name, World.Map.Rules.TileSet.Id); continue; } } } } }