Skip to content

Commit 05fab33

Browse files
authored
Select multiple objects (#81)
From Veger's PR: A PR by @DaleStan (ShadowTheAge#168) I had to fully rebase it due to all the changes we made in YAFC-CE. I tested it a bit, and it seems to be working. From the original PR: > Where logical (The Add production recipe, Add consumption recipe, Select raw recipe, and Add new milestone windows) this allows you to control-click to select multiple items. The currently selected items are highlighted, and you can control-click again to deselect them. After control-clicking at least one item, use the OK button to add all selected items and close the window. > > ![image](https://user-images.githubusercontent.com/21223975/175794752-09023b47-97f4-47a9-93c8-478d15d2342b.png) > > If you do not control-click, the single clicked-on item is selected and added, and the window is closed, as before.
2 parents ebfc653 + e64c310 commit 05fab33

16 files changed

+155
-60
lines changed

YAFC/Widgets/ImmediateWidgets.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,12 @@ public static void BuildInlineObjectListAndButton<T>(this ImGui gui, ICollection
150150
}
151151

152152
if (list.Count > count && gui.BuildButton("See full list") && gui.CloseDropdown()) {
153-
SelectObjectPanel.Select(list, header, select, ordering, allowNone);
153+
if (multiple) {
154+
SelectMultiObjectPanel.Select(list, header, select, ordering, allowNone, checkMark);
155+
}
156+
else {
157+
SelectSingleObjectPanel.Select(list, header, select, ordering, allowNone);
158+
}
154159
}
155160

156161
if (multiple && list.Count > 1) {

YAFC/Widgets/PseudoScreen.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,15 @@ public virtual bool KeyDown(SDL.SDL_Keysym key) {
6868
if (key.scancode == SDL.SDL_Scancode.SDL_SCANCODE_ESCAPE) {
6969
Close(false);
7070
}
71+
if (key.scancode == SDL.SDL_Scancode.SDL_SCANCODE_RETURN || key.scancode == SDL.SDL_Scancode.SDL_SCANCODE_RETURN2 || key.scancode == SDL.SDL_Scancode.SDL_SCANCODE_KP_ENTER) {
72+
ReturnPressed();
73+
}
7174

7275
return true;
7376
}
7477

78+
protected virtual void ReturnPressed() { }
79+
7580
public virtual bool TextInput(string input) {
7681
return true;
7782
}

YAFC/Windows/DependencyExplorer.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ public override void Build(ImGui gui) {
111111
using (gui.EnterRow()) {
112112
gui.BuildText("Currently inspecting:", Font.subheader);
113113
if (gui.BuildFactorioObjectButtonWithText(current)) {
114-
SelectObjectPanel.Select(Database.objects.all, "Select something", Change);
114+
SelectSingleObjectPanel.Select(Database.objects.all, "Select something", Change);
115115
}
116116

117117
gui.BuildText("(Click to change)", color: SchemeColor.BackgroundTextFaint);

YAFC/Windows/MainScreen.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ public void BuildSubHeader(ImGui gui, string text) {
340340
}
341341

342342
private void ShowNeie() {
343-
SelectObjectPanel.Select(Database.goods.all, "Open NEIE", NeverEnoughItemsPanel.Show);
343+
SelectSingleObjectPanel.Select(Database.goods.all, "Open NEIE", NeverEnoughItemsPanel.Show);
344344
}
345345

346346
private void SetSearch(SearchQuery searchQuery) {
@@ -420,7 +420,7 @@ private void SettingsDropdown(ImGui gui) {
420420
}
421421

422422
if (gui.BuildContextMenuButton("Dependency Explorer") && gui.CloseDropdown()) {
423-
SelectObjectPanel.Select(Database.objects.all, "Open Dependency Explorer", DependencyExplorer.Show);
423+
SelectSingleObjectPanel.Select(Database.objects.all, "Open Dependency Explorer", DependencyExplorer.Show);
424424
}
425425

426426
BuildSubHeader(gui, "Extra");

YAFC/Windows/MessageBox.cs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,12 @@
55
namespace YAFC {
66
public class MessageBox : PseudoScreen<bool> {
77
public MessageBox() : base(30f) { }
8-
private static readonly MessageBox Instance = new MessageBox();
98

109
private string title, message, yes, no;
1110

1211
public static void Show(Action<bool, bool> result, string title, string message, string yes, string no) {
13-
Instance.title = title;
14-
Instance.complete = result;
15-
Instance.message = message;
16-
Instance.yes = yes;
17-
Instance.no = no;
18-
_ = MainScreen.Instance.ShowPseudoScreen(Instance);
12+
MessageBox instance = new MessageBox { title = title, complete = result, message = message, yes = yes, no = no };
13+
_ = MainScreen.Instance.ShowPseudoScreen(instance);
1914
}
2015

2116
public static void Show(string title, string message, string yes) {

YAFC/Windows/MilestonesEditor.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Numerics;
1+
using System.Linq;
2+
using System.Numerics;
23
using YAFC.Model;
34
using YAFC.UI;
45

@@ -53,7 +54,7 @@ public override void Build(ImGui gui) {
5354
milestoneList.RebuildContents();
5455
}
5556
if (gui.BuildButton("Add milestone")) {
56-
SelectObjectPanel.Select(Database.objects.all, "Add new milestone", AddMilestone);
57+
SelectMultiObjectPanel.Select(Database.objects.all.Except(Project.current.settings.milestones), "Add new milestone", AddMilestone);
5758
}
5859
}
5960
}

YAFC/Windows/NeverEnoughItemsPanel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ public override void Build(ImGui gui) {
343343
}
344344

345345
if (gui.BuildFactorioObjectButton(gui.lastRect, current, SchemeColor.Grey)) {
346-
SelectObjectPanel.Select(Database.goods.all, "Select item", SetItem);
346+
SelectSingleObjectPanel.Select(Database.goods.all, "Select item", SetItem);
347347
}
348348

349349
using (var split = gui.EnterHorizontalSplit(2)) {

YAFC/Windows/ProjectPageSettingsPanel.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public class ProjectPageSettingsPanel : PseudoScreen {
2222
public static void Build(ImGui gui, ref string name, FactorioObject icon, Action<FactorioObject> setIcon) {
2323
_ = gui.BuildTextInput(name, out name, "Input name");
2424
if (gui.BuildFactorioObjectButton(icon, 4f, MilestoneDisplay.None, SchemeColor.Grey)) {
25-
SelectObjectPanel.Select(Database.objects.all, "Select icon", setIcon);
25+
SelectSingleObjectPanel.Select(Database.objects.all, "Select icon", setIcon);
2626
}
2727

2828
if (icon == null && gui.isBuilding) {
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Numerics;
5+
using YAFC.Model;
6+
using YAFC.UI;
7+
8+
namespace YAFC {
9+
public class SelectMultiObjectPanel : SelectObjectPanel<IEnumerable<FactorioObject>> {
10+
private static readonly SelectMultiObjectPanel Instance = new SelectMultiObjectPanel();
11+
private readonly HashSet<FactorioObject> results = new HashSet<FactorioObject>();
12+
private bool allowAutoClose;
13+
private Predicate<FactorioObject> checkMark;
14+
15+
public SelectMultiObjectPanel() : base() { }
16+
17+
public static void Select<T>(IEnumerable<T> list, string header, Action<T> select, bool allowNone = false, Predicate<T> checkMark = null) where T : FactorioObject {
18+
Select(list, header, select, DataUtils.DefaultOrdering, allowNone, checkMark);
19+
}
20+
21+
public static void Select<T>(IEnumerable<T> list, string header, Action<T> select, IComparer<T> ordering, bool allowNone = false, Predicate<T> checkMark = null) where T : FactorioObject {
22+
Instance.allowAutoClose = true;
23+
Instance.results.Clear();
24+
Instance.checkMark = (o) => checkMark?.Invoke((T)o) ?? false; // This is messy, but pushing T all the way around the call stack and type tree was messier.
25+
Instance.Select(list, header, select, ordering, (xs, selectItem) => {
26+
foreach (var x in xs ?? Enumerable.Empty<T>()) {
27+
selectItem(x);
28+
}
29+
}, allowNone);
30+
}
31+
32+
protected override void NonNullElementDrawer(ImGui gui, FactorioObject element, int index) {
33+
bool click = gui.BuildFactorioObjectButton(element, display: MilestoneDisplay.Contained, bgColor: results.Contains(element) ? SchemeColor.Primary : SchemeColor.None, extendHeader: extendHeader);
34+
35+
if (checkMark(element)) {
36+
gui.DrawIcon(Rect.SideRect(gui.lastRect.TopLeft + new Vector2(1, 0), gui.lastRect.BottomRight - new Vector2(0, 1)), Icon.Check, SchemeColor.Green);
37+
}
38+
39+
if (click) {
40+
if (!results.Add(element)) {
41+
results.Remove(element);
42+
}
43+
if (!InputSystem.Instance.control && allowAutoClose) {
44+
CloseWithResult(results);
45+
}
46+
allowAutoClose = false;
47+
}
48+
}
49+
50+
public override void Build(ImGui gui) {
51+
base.Build(gui);
52+
using (gui.EnterGroup(default, RectAllocator.Center)) {
53+
if (gui.BuildButton("OK")) {
54+
CloseWithResult(results);
55+
}
56+
gui.BuildText("Hint: ctrl+click to select multiple", color: SchemeColor.BackgroundTextFaint);
57+
}
58+
}
59+
60+
protected override void ReturnPressed() {
61+
CloseWithResult(results);
62+
}
63+
}
64+
}

YAFC/Windows/SelectObjectPanel.cs

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,64 +6,64 @@
66
using YAFC.UI;
77

88
namespace YAFC {
9-
public class SelectObjectPanel : PseudoScreen<FactorioObject> {
10-
private static readonly SelectObjectPanel Instance = new SelectObjectPanel();
11-
private readonly SearchableList<FactorioObject> list;
12-
private string header;
13-
private Rect searchBox;
14-
private bool extendHeader;
15-
public SelectObjectPanel() : base(40f) {
16-
list = new SearchableList<FactorioObject>(30, new Vector2(2.5f, 2.5f), ElementDrawer, ElementFilter);
17-
}
9+
public abstract class SelectObjectPanel<T> : PseudoScreen<T> {
10+
protected readonly SearchableList<FactorioObject> list;
11+
protected string header;
12+
protected Rect searchBox;
13+
protected bool extendHeader;
1814

19-
private bool ElementFilter(FactorioObject data, SearchQuery query) {
20-
return data.Match(query);
15+
protected SelectObjectPanel() : base(40f) {
16+
list = new SearchableList<FactorioObject>(30, new Vector2(2.5f, 2.5f), ElementDrawer, ElementFilter);
2117
}
2218

23-
public static void Select<T>(IEnumerable<T> list, string header, Action<T> select, IComparer<T> ordering, bool allowNone) where T : FactorioObject {
24-
_ = MainScreen.Instance.ShowPseudoScreen(Instance);
25-
Instance.extendHeader = typeof(T) == typeof(FactorioObject);
26-
List<T> data = new List<T>(list);
19+
protected void Select<U>(IEnumerable<U> list, string header, Action<U> select, IComparer<U> ordering, Action<T, Action<FactorioObject>> process, bool allowNone) where U : FactorioObject {
20+
_ = MainScreen.Instance.ShowPseudoScreen(this);
21+
extendHeader = typeof(U) == typeof(FactorioObject);
22+
List<U> data = new List<U>(list);
2723
data.Sort(ordering);
2824
if (allowNone) {
2925
data.Insert(0, null);
3026
}
3127

32-
Instance.list.filter = default;
33-
Instance.list.data = data;
34-
Instance.header = header;
35-
Instance.Rebuild();
36-
Instance.complete = (selected, x) => {
37-
if (x is T t) {
38-
if (ordering is DataUtils.FavoritesComparer<T> favoritesComparer) {
39-
favoritesComparer.AddToFavorite(t);
28+
this.list.filter = default;
29+
this.list.data = data;
30+
this.header = header;
31+
Rebuild();
32+
complete = (selected, x) => process(x, x => {
33+
if (x is U u) {
34+
if (ordering is DataUtils.FavoritesComparer<U> favoritesComparer) {
35+
favoritesComparer.AddToFavorite(u);
4036
}
4137

42-
select(t);
38+
select(u);
4339
}
4440
else if (allowNone && selected) {
4541
select(null);
4642
}
47-
};
43+
});
4844
}
4945

50-
public static void Select<T>(IEnumerable<T> list, string header, Action<T> select, bool allowNone = false) where T : FactorioObject {
51-
Select(list, header, select, DataUtils.DefaultOrdering, allowNone);
46+
protected void Select<U>(IEnumerable<U> list, string header, Action<U> select, Action<T, Action<FactorioObject>> process, bool allowNone = false) where U : FactorioObject {
47+
Select(list, header, select, DataUtils.DefaultOrdering, process, allowNone);
5248
}
5349

5450
private void ElementDrawer(ImGui gui, FactorioObject element, int index) {
5551
if (element == null) {
5652
if (gui.BuildRedButton(Icon.Close)) {
57-
CloseWithResult(null);
53+
CloseWithResult(default);
5854
}
5955
}
6056
else {
61-
if (gui.BuildFactorioObjectButton(element, display: MilestoneDisplay.Contained, extendHeader: extendHeader)) {
62-
CloseWithResult(element);
63-
}
57+
NonNullElementDrawer(gui, element, index);
6458
}
6559
}
6660

61+
protected abstract void NonNullElementDrawer(ImGui gui, FactorioObject element, int index);
62+
63+
private bool ElementFilter(FactorioObject data, SearchQuery query) {
64+
return data.Match(query);
65+
}
66+
6767
public override void Build(ImGui gui) {
6868
BuildHeader(gui, header);
6969
if (gui.BuildSearchBox(list.filter, out var newFilter, "Start typing for search")) {

0 commit comments

Comments
 (0)