Skip to content

Commit be0f6e3

Browse files
authored
Merge pull request #1733 from CitiesSkylinesMods/improvement/customizable-scroll-behavior-closed-dropdown
Custom Dropdown - prevent unwanted option change on mouse wheel scrool
2 parents d22b8d0 + e9653fe commit be0f6e3

File tree

6 files changed

+139
-5
lines changed

6 files changed

+139
-5
lines changed

TLM/TLM/State/OptionsTabs/GeneralTab_LocalisationGroup.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace TrafficManager.State {
1010
using TrafficManager.UI;
1111
using TrafficManager.UI.Helpers;
1212
using TrafficManager.UI.Textures;
13+
using TrafficManager.Util.Extensions;
1314

1415
/// <summary>
1516
/// Adds localisation options to General options tab.
@@ -64,7 +65,7 @@ private static void AddLanguageDropDown(UIHelperBase group) {
6465
}
6566
}
6667

67-
group.AddDropdown(
68+
group.AddCustomDropDown(
6869
text: T("General.Dropdown:Select language") + ":",
6970
options: languageLabels,
7071
defaultSelection: languageIndex,
@@ -84,7 +85,7 @@ private static void AddRoadSignThemeDropDown(UIHelperBase group) {
8485
int selectedThemeIndex = themeNames.FindIndex(x => x == mainConfig.RoadSignTheme);
8586
int defaultSignsThemeIndex = RoadSignThemeManager.Instance.FindDefaultThemeIndex(mainConfig.DisplaySpeedLimitsMph);
8687

87-
_roadSignsThemeDropdown = group.AddDropdown(
88+
_roadSignsThemeDropdown = group.AddCustomDropDown(
8889
text: TSpeedLimits("General.Dropdown:Road signs theme") + ":",
8990
options: themeOptions,
9091
defaultSelection: selectedThemeIndex >= 0 ? selectedThemeIndex : defaultSignsThemeIndex,

TLM/TLM/TLM.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@
184184
<Compile Include="TrafficLight\Impl\ITrafficLightContainer.cs" />
185185
<Compile Include="TrafficLight\Impl\TrafficLightSimulation.cs" />
186186
<Compile Include="UI\AllowDespawn\AllowDespawnPanel.cs" />
187+
<Compile Include="UI\Helpers\CustomDownDown.cs" />
187188
<Compile Include="UI\Helpers\DebugOverlay.cs" />
188189
<Compile Include="UI\DebugSwitches\DebugSwitchPanel.cs" />
189190
<Compile Include="UI\DebugSwitches\DebugSwitchCheckboxOption.cs" />
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
namespace TrafficManager.UI.Helpers;
2+
3+
using ColossalFramework.UI;
4+
using UnityEngine;
5+
6+
/// <summary>
7+
/// Custom version of vanilla dropdown with customizable mouse wheel behavior on hover closed dropdown control
8+
/// </summary>
9+
public class CustomDownDown : UIDropDown {
10+
public static GameObject ScrollTemplate;
11+
12+
/// <summary>
13+
/// Use mouse wheel when dropdown is closed to change selection
14+
/// Unlike in vanilla, default is False
15+
/// </summary>
16+
public bool MouseWheelSelectItem { get; set; } = false;
17+
18+
public override void OnDestroy() {
19+
if (ScrollTemplate) {
20+
Destroy(ScrollTemplate);
21+
ScrollTemplate = null;
22+
}
23+
24+
base.OnDestroy();
25+
}
26+
27+
protected override void OnMouseWheel(UIMouseEventParameter p) {
28+
if (MouseWheelSelectItem) {
29+
// do default action
30+
base.OnMouseWheel(p);
31+
} else {
32+
// mark as used to prevent scrolling page or selecting different items in the dropdown!
33+
p.Use();
34+
}
35+
}
36+
}

TLM/TLM/UI/Helpers/DropDownOption.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
namespace TrafficManager.UI.Helpers {
22
using ICities;
33
using ColossalFramework.UI;
4-
using TrafficManager.State;
54
using CSUtil.Commons;
65
using System;
76
using System.Linq;
87
using TrafficManager.API.Traffic.Enums;
98
using TrafficManager.Util;
9+
using TrafficManager.Util.Extensions;
1010

1111
public class DropDownOption<TEnum> : SerializableUIOptionBase<TEnum, UIDropDown, DropDownOption<TEnum>>
1212
where TEnum : struct, Enum, IConvertible {
@@ -71,7 +71,7 @@ public override TEnum Value {
7171
}
7272

7373
public override DropDownOption<TEnum> AddUI(UIHelperBase container) {
74-
_ui = container.AddDropdown(
74+
_ui = container.AddCustomDropDown(
7575
text: Translate(Label) + ":",
7676
options: GetTranslatedItems(),
7777
defaultSelection: IndexOf(Value),

TLM/TLM/Util/Extensions/UIHelperExtensions.cs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
namespace TrafficManager.Util.Extensions {
22
using ColossalFramework.UI;
33
using ICities;
4+
using TrafficManager.Lifecycle;
5+
using TrafficManager.UI.Helpers;
46
using UnityEngine;
57

68
internal static class UIHelperExtensions {
@@ -11,5 +13,99 @@ public static T AddComponent<T>(this UIHelperBase container)
1113

1214
public static T AddUIComponent<T>(this UIHelperBase container)
1315
where T : UIComponent => container.GetSelf().AddUIComponent<T>();
16+
17+
public static CustomDownDown AddCustomDropDown(this UIHelperBase container,
18+
string text,
19+
string[] options,
20+
int defaultSelection,
21+
OnDropdownSelectionChanged eventCallback
22+
) {
23+
if (eventCallback != null && !string.IsNullOrEmpty(text)) {
24+
UIComponent root = container.GetSelf();
25+
UIPanel uipanel = root.AddUIComponent<UIPanel>();
26+
uipanel.area = new Vector4(14, 0, 232, 72);
27+
uipanel.autoLayoutDirection = LayoutDirection.Vertical;
28+
uipanel.autoLayoutPadding = new RectOffset(1, 0, 2, 0);
29+
uipanel.autoLayout = true;
30+
31+
var label = uipanel.AddUIComponent<UILabel>();
32+
label.text = text;
33+
label.name = "Label";
34+
35+
var dropdown = uipanel.AddUIComponent<CustomDownDown>();
36+
dropdown.name = "Dropdown";
37+
dropdown.area = new Vector4(0, 24, 225, 38);
38+
dropdown.items = options;
39+
dropdown.selectedIndex = defaultSelection;
40+
dropdown.triggerButton = dropdown;
41+
dropdown.relativePosition = new Vector3(0, 24, 0);
42+
dropdown.itemPadding = new RectOffset(14, 14, 0, 0);
43+
dropdown.listPadding = new RectOffset(4, 4, 4, 4);
44+
dropdown.textFieldPadding = new RectOffset(14, 40, 7, 4);
45+
dropdown.itemHeight = 24;
46+
dropdown.listHeight = 200;
47+
dropdown.textScale = 1.25f;
48+
dropdown.itemHover = "ListItemHover";
49+
dropdown.itemHighlight = "ListItemHighlight";
50+
dropdown.focusedFgSprite = "OptionsDropboxFocused";
51+
dropdown.focusedBgSprite = "OptionsDropboxHovered";
52+
dropdown.hoveredBgSprite = "OptionsDropboxHovered";
53+
dropdown.normalBgSprite = "OptionsDropbox";
54+
dropdown.listBackground = "OptionsDropboxListbox";
55+
dropdown.popupColor = Color.white;
56+
dropdown.popupTextColor = new Color32(170, 170, 170, 255);
57+
dropdown.eventSelectedIndexChanged += delegate(UIComponent c, int sel) {
58+
eventCallback(sel);
59+
};
60+
61+
if (CustomDownDown.ScrollTemplate) {
62+
// reuse gameObject template
63+
dropdown.listScrollbar = CustomDownDown.ScrollTemplate.GetComponent<UIScrollbar>();
64+
} else {
65+
UIScrollbar scrollbar = CreateScrollbar();
66+
// cache gameObject for later reuse
67+
CustomDownDown.ScrollTemplate = scrollbar.gameObject;
68+
dropdown.listScrollbar = scrollbar;
69+
}
70+
71+
return dropdown;
72+
}
73+
return null;
74+
}
75+
76+
private static UIScrollbar CreateScrollbar() {
77+
UIScrollbar verticalScrollbar = new GameObject("TMPE_ScrollbarV").AddComponent<UIScrollbar>();
78+
// attach to lifecycle gameObject for easier search in gameObject tree
79+
verticalScrollbar.gameObject.transform.SetParent(TMPELifecycle.Instance.gameObject.transform);
80+
81+
verticalScrollbar.name = "TMPE_ScrollbarV";
82+
verticalScrollbar.width = 25;
83+
verticalScrollbar.height = 200;
84+
verticalScrollbar.orientation = UIOrientation.Vertical;
85+
verticalScrollbar.pivot = UIPivotPoint.TopLeft;
86+
verticalScrollbar.minValue = 0;
87+
verticalScrollbar.maxValue = 87;
88+
verticalScrollbar.value = 0;
89+
verticalScrollbar.incrementAmount = 50;
90+
verticalScrollbar.autoHide = true;
91+
92+
UISlicedSprite trackSprite = verticalScrollbar.AddUIComponent<UISlicedSprite>();
93+
trackSprite.relativePosition = Vector2.zero;
94+
trackSprite.autoSize = true;
95+
trackSprite.size = trackSprite.parent.size;
96+
trackSprite.fillDirection = UIFillDirection.Vertical;
97+
trackSprite.spriteName = "ScrollbarTrack";
98+
verticalScrollbar.trackObject = trackSprite;
99+
100+
UISlicedSprite thumbSprite = trackSprite.AddUIComponent<UISlicedSprite>();
101+
thumbSprite.relativePosition = Vector2.zero;
102+
thumbSprite.fillDirection = UIFillDirection.Vertical;
103+
thumbSprite.autoSize = true;
104+
thumbSprite.width = thumbSprite.parent.width - 6;
105+
thumbSprite.spriteName = "ScrollbarThumb";
106+
verticalScrollbar.thumbObject = thumbSprite;
107+
108+
return verticalScrollbar;
109+
}
14110
}
15111
}

TLM/TLM/Util/Shortcuts.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ internal static void AssertNotNull(object obj, string m = "") {
146146
/// <exception cref="ArgumentException">
147147
/// Thrown if <typeparamref name="T"/> is not some kind of <see cref="Enum"/>.
148148
/// </exception>
149-
internal static void AssertNotNone<T>(T value, string m = "") where T: IEquatable<T> {
149+
internal static void AssertNotNone<T>(T value, string m = "") where T: Enum {
150150
if (!typeof(T).IsEnum)
151151
throw new ArgumentException($"Type '{typeof(T).FullName}' is not an enum");
152152

0 commit comments

Comments
 (0)