redesign menu options, all menu options with input now have a default value

This commit is contained in:
UnknownShadow200 2018-04-16 16:14:29 +10:00
parent 28f4bb3e97
commit 41a8f97a88
8 changed files with 215 additions and 303 deletions

View File

@ -15,7 +15,10 @@ namespace ClassicalSharp.Gui.Screens {
public override void Init() {
base.Init();
ContextRecreated();
MakeValidators();
validators = new MenuInputValidator[widgets.Length];
validators[2] = new EnumValidator(typeof(ViewDist));
validators[7] = new EnumValidator(typeof(FpsLimitMethod));
}
protected override void ContextRecreated() {
@ -38,7 +41,6 @@ namespace ClassicalSharp.Gui.Screens {
ButtonWidget.Create(game, 400, "Controls...", titleFont, SwitchClassic)
.SetLocation(Anchor.Centre, Anchor.Max, 0, 95),
MakeBack(400, "Done", 25, titleFont, SwitchPause),
null, null,
};
}
@ -90,21 +92,5 @@ namespace ClassicalSharp.Gui.Screens {
}
static void SwitchClassic(Game g, Widget w) { g.Gui.SetNewScreen(new ClassicKeyBindingsScreen(g)); }
void MakeValidators() {
IServerConnection network = game.Server;
validators = new MenuInputValidator[] {
null,
null,
new EnumValidator(typeof(ViewDist)),
null,
null,
null,
null,
new EnumValidator(typeof(FpsLimitMethod)),
null,
};
}
}
}

View File

@ -7,25 +7,44 @@ using ClassicalSharp.Renderers;
using OpenTK.Input;
namespace ClassicalSharp.Gui.Screens {
public class EnvSettingsScreen : MenuOptionsScreen {
public class EnvSettingsScreen : ExtMenuOptionsScreen {
public EnvSettingsScreen(Game game) : base(game) {
}
string[] defaultValues;
int defaultIndex;
public override void Init() {
base.Init();
ContextRecreated();
MakeDefaultValues();
MakeValidators();
validators = new MenuInputValidator[widgets.Length];
defaultValues = new string[widgets.Length];
validators = new MenuInputValidator[widgets.Length];
validators[0] = new HexColourValidator();
defaultValues[0] = WorldEnv.DefaultCloudsColour.ToRGBHexString();
validators[1] = new HexColourValidator();
defaultValues[1] = WorldEnv.DefaultSkyColour.ToRGBHexString();
validators[2] = new HexColourValidator();
defaultValues[2] = WorldEnv.DefaultFogColour.ToRGBHexString();
validators[3] = new RealValidator(0, 1000);
defaultValues[3] = "1";
validators[4] = new IntegerValidator(-10000, 10000);
defaultValues[4] = (game.World.Height + 2).ToString();
validators[5] = new HexColourValidator();
defaultValues[5] = WorldEnv.DefaultSunlight.ToRGBHexString();
validators[6] = new HexColourValidator();
defaultValues[6] = WorldEnv.DefaultShadowlight.ToRGBHexString();
validators[7] = new EnumValidator(typeof(Weather));
validators[8] = new RealValidator(-100, 100);
defaultValues[8] = "1";
validators[9] = new IntegerValidator(-2048, 2048);
defaultValues[9] = (game.World.Height / 2).ToString();
}
protected override void ContextRecreated() {
ClickHandler onClick = OnButtonClick;
ClickHandler onEnum = OnEnumClick;
widgets = new Widget[] {
MakeOpt(-1, -150, "Clouds col", onClick, GetCloudsCol, SetCloudsCol),
MakeOpt(-1, -100, "Sky col", onClick, GetSkyCol, SetSkyCol),
@ -55,7 +74,7 @@ namespace ClassicalSharp.Gui.Screens {
static string GetCloudsSpeed(Game g) { return g.World.Env.CloudsSpeed.ToString("F2"); }
static void SetCloudsSpeed(Game g, string v) { g.World.Env.SetCloudsSpeed(Utils.ParseDecimal(v)); }
static string GetCloudsHeight(Game g) { return g.World.Env.CloudHeight.ToString(); }
static void SetCloudsHeight(Game g, string v) { g.World.Env.SetCloudsLevel(Int32.Parse(v)); }
@ -73,58 +92,5 @@ namespace ClassicalSharp.Gui.Screens {
static string GetEdgeHeight(Game g) { return g.World.Env.EdgeHeight.ToString(); }
static void SetEdgeHeight(Game g, string v) { g.World.Env.SetEdgeLevel(Int32.Parse(v)); }
void MakeDefaultValues() {
defaultIndex = widgets.Length - 3;
defaultValues = new string[] {
WorldEnv.DefaultCloudsColour.ToRGBHexString(),
WorldEnv.DefaultSkyColour.ToRGBHexString(),
WorldEnv.DefaultFogColour.ToRGBHexString(),
(1).ToString(),
(game.World.Height + 2).ToString(),
WorldEnv.DefaultSunlight.ToRGBHexString(),
WorldEnv.DefaultShadowlight.ToRGBHexString(),
null,
(1).ToString(),
(game.World.Height / 2).ToString(),
};
}
void MakeValidators() {
validators = new MenuInputValidator[] {
new HexColourValidator(),
new HexColourValidator(),
new HexColourValidator(),
new RealValidator(0, 1000),
new IntegerValidator(-10000, 10000),
new HexColourValidator(),
new HexColourValidator(),
new EnumValidator(typeof(Weather)),
new RealValidator(-100, 100),
new IntegerValidator(-2048, 2048),
};
}
protected override void InputClosed() {
base.InputClosed();
if (widgets[defaultIndex] != null)
widgets[defaultIndex].Dispose();
widgets[defaultIndex] = null;
}
protected override void InputOpened() {
widgets[defaultIndex] = ButtonWidget.Create(game, 200, "Default value", titleFont, DefaultButtonClick)
.SetLocation(Anchor.Centre, Anchor.Centre, 0, 150);
}
void DefaultButtonClick(Game game, Widget widget) {
int index = IndexOfWidget(activeButton);
string defValue = defaultValues[index];
input.Clear();
input.Append(defValue);
}
}
}

View File

@ -6,7 +6,7 @@ using ClassicalSharp.Gui.Widgets;
using ClassicalSharp.Textures;
namespace ClassicalSharp.Gui.Screens {
public class GraphicsOptionsScreen : MenuOptionsScreen {
public class GraphicsOptionsScreen : ExtMenuOptionsScreen {
public GraphicsOptionsScreen(Game game) : base(game) {
}
@ -14,7 +14,14 @@ namespace ClassicalSharp.Gui.Screens {
public override void Init() {
base.Init();
ContextRecreated();
MakeValidators();
validators = new MenuInputValidator[widgets.Length];
defaultValues = new string[widgets.Length];
validators[0] = new EnumValidator(typeof(FpsLimitMethod));
validators[1] = new IntegerValidator(8, 4096);
defaultValues[1] = "512";
validators[3] = new EnumValidator(typeof(NameMode));
validators[4] = new EnumValidator(typeof(EntityShadow));
MakeDescriptions();
}
@ -33,7 +40,7 @@ namespace ClassicalSharp.Gui.Screens {
MakeOpt(1, 50, "Mipmaps", onBool, GetMipmaps, SetMipmaps),
MakeBack(false, titleFont, SwitchOptions),
null, null,
null, null, null,
};
}
@ -85,18 +92,6 @@ namespace ClassicalSharp.Gui.Screens {
}
}
void MakeValidators() {
validators = new MenuInputValidator[] {
new EnumValidator(typeof(FpsLimitMethod)),
new IntegerValidator(8, 4096),
null,
new EnumValidator(typeof(NameMode)),
new EnumValidator(typeof(EntityShadow)),
null,
};
}
void MakeDescriptions() {
descriptions = new string[widgets.Length][];
descriptions[0] = new string[] {

View File

@ -3,15 +3,27 @@ using System;
using ClassicalSharp.Gui.Widgets;
namespace ClassicalSharp.Gui.Screens {
public class GuiOptionsScreen : MenuOptionsScreen {
public class GuiOptionsScreen : ExtMenuOptionsScreen {
public GuiOptionsScreen(Game game) : base(game) {
}
public override void Init() {
base.Init();
ContextRecreated();
MakeValidators();
ContextRecreated();
validators = new MenuInputValidator[widgets.Length];
defaultValues = new string[widgets.Length];
validators[2] = new RealValidator(0.25f, 4f);
defaultValues[2] = "1";
validators[3] = new RealValidator(0.25f, 4f);
defaultValues[3] = "1";
validators[6] = new RealValidator(0.25f, 4f);
defaultValues[6] = "1";
validators[7] = new IntegerValidator(0, 30);
defaultValues[7] = "10";
validators[9] = new StringValidator();
defaultValues[9] = "Arial";
}
protected override void ContextRecreated() {
@ -32,7 +44,7 @@ namespace ClassicalSharp.Gui.Screens {
MakeOpt(1, 50, "Font", onClick, GetFont, SetFont),
MakeBack(false, titleFont, SwitchOptions),
null, null,
null, null, null,
};
}
@ -93,21 +105,5 @@ namespace ClassicalSharp.Gui.Screens {
selectedI = -1;
HandlesMouseMove(game.Mouse.X, game.Mouse.Y);
}
void MakeValidators() {
validators = new MenuInputValidator[] {
null,
null,
new RealValidator(0.25f, 4f),
new RealValidator(0.25f, 4f),
null,
null,
new RealValidator(0.25f, 4f),
new IntegerValidator(0, 30),
null,
new StringValidator(),
};
}
}
}

View File

@ -7,22 +7,44 @@ using ClassicalSharp.Singleplayer;
using OpenTK.Input;
namespace ClassicalSharp.Gui.Screens {
public class HacksSettingsScreen : MenuOptionsScreen {
public class HacksSettingsScreen : ExtMenuOptionsScreen {
public HacksSettingsScreen(Game game) : base(game) {
}
string[] defaultValues;
int defaultIndex;
public override void Init() {
base.Init();
ContextRecreated();
MakeDefaultValues();
MakeValidators();
MakeDescriptions();
game.Events.HackPermissionsChanged += CheckHacksAllowed;
MakeDescriptions();
validators = new MenuInputValidator[widgets.Length];
defaultValues = new string[widgets.Length];
validators[1] = new RealValidator(0.1f, 50);
defaultValues[1] = "10";
validators[3] = new RealValidator(0.1f, 2048f);
defaultValues[3] = (1.233f).ToString();
validators[9] = new IntegerValidator(1, 150);
defaultValues[9] = "70";
}
public override void Dispose() {
base.Dispose();
game.Events.HackPermissionsChanged -= CheckHacksAllowed;
}
void CheckHacksAllowed(object sender, EventArgs e) {
for (int i = 0; i < widgets.Length; i++) {
if (widgets[i] == null) continue;
widgets[i].Disabled = false;
}
LocalPlayer p = game.LocalPlayer;
bool noGlobalHacks = !p.Hacks.CanAnyHacks || !p.Hacks.Enabled;
widgets[3].Disabled = noGlobalHacks || !p.Hacks.CanSpeed;
widgets[4].Disabled = noGlobalHacks || !p.Hacks.CanSpeed;
widgets[5].Disabled = noGlobalHacks || !p.Hacks.CanSpeed;
widgets[7].Disabled = noGlobalHacks || !p.Hacks.CanPushbackBlocks;
}
protected override void ContextRecreated() {
@ -41,10 +63,9 @@ namespace ClassicalSharp.Gui.Screens {
MakeOpt(1, -50, "Pushback placing", onBool, GetPushback, SetPushback),
MakeOpt(1, 0, "Noclip slide", onBool, GetSlide, SetSlide),
MakeOpt(1, 50, "Field of view", onClick, GetFOV, SetFOV),
null,
MakeBack(false, titleFont, SwitchOptions),
null, null,
null, null, null,
};
CheckHacksAllowed(null, null);
}
@ -107,92 +128,30 @@ namespace ClassicalSharp.Gui.Screens {
g.UpdateProjection();
}
void CheckHacksAllowed(object sender, EventArgs e) {
for (int i = 0; i < widgets.Length; i++) {
if (widgets[i] == null) continue;
widgets[i].Disabled = false;
}
LocalPlayer p = game.LocalPlayer;
bool noGlobalHacks = !p.Hacks.CanAnyHacks || !p.Hacks.Enabled;
widgets[3].Disabled = noGlobalHacks || !p.Hacks.CanSpeed;
widgets[4].Disabled = noGlobalHacks || !p.Hacks.CanSpeed;
widgets[5].Disabled = noGlobalHacks || !p.Hacks.CanSpeed;
widgets[7].Disabled = noGlobalHacks || !p.Hacks.CanPushbackBlocks;
}
public override void Dispose() {
base.Dispose();
game.Events.HackPermissionsChanged -= CheckHacksAllowed;
}
void MakeDefaultValues() {
defaultIndex = widgets.Length - 4;
defaultValues = new string[widgets.Length];
defaultValues[1] = "10";
defaultValues[3] = (1.233f).ToString();
defaultValues[9] = "70";
}
void MakeValidators() {
validators = new MenuInputValidator[] {
null,
new RealValidator(0.1f, 50),
null,
new RealValidator(0.1f, 2048f),
null,
null,
null,
null,
null,
new IntegerValidator(1, 150),
};
}
void MakeDescriptions() {
descriptions = new string[widgets.Length][];
descriptions[2] = new string[] {
string[][] descs = new string[widgets.Length][];
descs[2] = new string[] {
"&eIf &fON&e, then the third person cameras will limit",
"&etheir zoom distance if they hit a solid block.",
};
descriptions[3] = new string[] {
descs[3] = new string[] {
"&eSets how many blocks high you can jump up.",
"&eNote: You jump much higher when holding down the Speed key binding.",
};
descriptions[6] = new string[] {
descs[6] = new string[] {
"&eIf &fON&e, then water/lava can be placed and",
"&edeleted the same way as any other block.",
};
descriptions[7] = new string[] {
descs[7] = new string[] {
"&eIf &fON&e, placing blocks that intersect your own position cause",
"&ethe block to be placed, and you to be moved out of the way.",
"&fThis is mainly useful for quick pillaring/towering.",
};
descriptions[8] = new string[] {
descs[8] = new string[] {
"&eIf &fOFF&e, you will immediately stop when in noclip",
"&emode and no movement keys are held down.",
};
}
protected override void InputClosed() {
base.InputClosed();
if (widgets[defaultIndex] != null)
widgets[defaultIndex].Dispose();
widgets[defaultIndex] = null;
}
protected override void InputOpened() {
widgets[defaultIndex] = ButtonWidget.Create(game, 200, "Default value", titleFont, DefaultButtonClick)
.SetLocation(Anchor.Centre, Anchor.Centre, 0, 150);
}
void DefaultButtonClick(Game game, Widget widget) {
int index = IndexOfWidget(activeButton);
string defValue = defaultValues[index];
input.Clear();
input.Append(defValue);
descriptions = descs;
}
}
}

View File

@ -10,7 +10,6 @@ namespace ClassicalSharp.Gui.Screens {
public MenuOptionsScreen(Game game) : base(game) {
}
protected InputWidget input;
protected MenuInputValidator[] validators;
protected string[][] descriptions;
protected TextGroupWidget extendedHelp;
@ -33,31 +32,6 @@ namespace ClassicalSharp.Gui.Screens {
game.Graphics.Texturing = false;
}
public override void Init() {
base.Init();
game.Keyboard.KeyRepeat = true;
}
public override bool HandlesKeyPress(char key) {
if (input == null) return true;
return input.HandlesKeyPress(key);
}
public override bool HandlesKeyDown(Key key) {
if (input != null) {
if (input.HandlesKeyDown(key)) return true;
if (key == Key.Enter || key == Key.KeypadEnter) {
ChangeSetting(); return true;
}
}
return base.HandlesKeyDown(key);
}
public override bool HandlesKeyUp(Key key) {
if (input == null) return true;
return input.HandlesKeyUp(key);
}
public override void OnResize(int width, int height) {
base.OnResize(width, height);
if (extendedHelp == null) return;
@ -69,15 +43,9 @@ namespace ClassicalSharp.Gui.Screens {
protected override void ContextLost() {
base.ContextLost();
InputClosed();
DisposeExtendedHelp();
}
public override void Dispose() {
game.Keyboard.KeyRepeat = false;
base.Dispose();
}
protected ButtonWidget activeButton;
protected int selectedI = -1;
@ -100,18 +68,6 @@ namespace ClassicalSharp.Gui.Screens {
ShowExtendedHelp();
}
protected virtual void InputOpened() { }
protected virtual void InputClosed() {
if (input != null) input.Dispose();
widgets[widgets.Length - 2] = null;
input = null;
int okIndex = widgets.Length - 1;
if (widgets[okIndex] != null) widgets[okIndex].Dispose();
widgets[okIndex] = null;
}
protected ButtonWidget MakeOpt(int dir, int y, string optName, ClickHandler onClick,
ButtonValueGetter getter, ButtonValueSetter setter) {
ButtonWidget btn = ButtonWidget.Create(game, 300, optName + ": " + getter(game), titleFont, onClick)
@ -128,9 +84,8 @@ namespace ClassicalSharp.Gui.Screens {
return v == "ON";
}
void ShowExtendedHelp() {
if (input != null || descriptions == null) return;
if (selectedI < 0 || selectedI >= descriptions.Length) return;
protected virtual void ShowExtendedHelp() {
if (descriptions == null || selectedI < 0 || selectedI >= descriptions.Length) return;
string[] desc = descriptions[selectedI];
if (desc == null) return;
@ -152,16 +107,12 @@ namespace ClassicalSharp.Gui.Screens {
extendedHelp.Reposition();
}
void DisposeExtendedHelp() {
protected void DisposeExtendedHelp() {
if (extendedHelp == null) return;
extendedHelp.Dispose();
extendedHelp = null;
}
void OnOKButtonClick(Game game, Widget widget) {
ChangeSetting();
}
protected void OnBoolClick(Game game, Widget widget) {
ButtonWidget button = (ButtonWidget)widget;
DisposeExtendedHelp();
@ -178,34 +129,9 @@ namespace ClassicalSharp.Gui.Screens {
int index = IndexOfWidget(button);
MenuInputValidator validator = validators[index];
Type type = ((EnumValidator)validator).EnumType;
HandleEnumOption(button, type);
}
protected void OnButtonClick(Game game, Widget widget) {
ButtonWidget button = (ButtonWidget)widget;
DisposeExtendedHelp();
int index = IndexOfWidget(button);
MenuInputValidator validator = validators[index];
activeButton = button;
InputClosed();
input = MenuInputWidget.Create(game, 400, 30, button.GetValue(game), textFont, validator)
.SetLocation(Anchor.Centre, Anchor.Centre, 0, 110);
input.ShowCaret = true;
widgets[widgets.Length - 2] = input;
widgets[widgets.Length - 1] = ButtonWidget.Create(game, 40, "OK", titleFont, OnOKButtonClick)
.SetLocation(Anchor.Centre, Anchor.Centre, 240, 110);
InputOpened();
UpdateDescription(activeButton);
}
void HandleEnumOption(ButtonWidget button, Type type) {
string rawName = button.GetValue(game);
int value = (int)Enum.Parse(type, rawName, true);
value++;
int value = (int)Enum.Parse(type, rawName, true) + 1;
// go back to first value
if (!Enum.IsDefined(type, value)) value = 0;
@ -213,18 +139,7 @@ namespace ClassicalSharp.Gui.Screens {
UpdateDescription(button);
}
void ChangeSetting() {
string text = input.Text.ToString();
if (((MenuInputWidget)input).Validator.IsValidValue(text)) {
SetButtonValue(activeButton, text);
}
UpdateDescription(activeButton);
activeButton = null;
InputClosed();
}
void SetButtonValue(ButtonWidget btn, string text) {
protected void SetButtonValue(ButtonWidget btn, string text) {
btn.SetValue(game, text);
int index = IndexOfWidget(btn);
// e.g. changing FPS invalidates all widgets
@ -238,4 +153,105 @@ namespace ClassicalSharp.Gui.Screens {
Options.Set(OptionsKey.FpsLimit, v);
}
}
public abstract class ExtMenuOptionsScreen : MenuOptionsScreen {
public ExtMenuOptionsScreen(Game game) : base(game) {
}
protected InputWidget input;
protected string[] defaultValues;
public override void Init() {
base.Init();
game.Keyboard.KeyRepeat = true;
}
public override bool HandlesKeyPress(char key) {
if (input == null) return true;
return input.HandlesKeyPress(key);
}
public override bool HandlesKeyDown(Key key) {
if (input != null) {
if (input.HandlesKeyDown(key)) return true;
if (key == Key.Enter || key == Key.KeypadEnter) {
EnterInput(); return true;
}
}
return base.HandlesKeyDown(key);
}
public override bool HandlesKeyUp(Key key) {
if (input == null) return true;
return input.HandlesKeyUp(key);
}
protected override void ContextLost() {
base.ContextLost();
input = null;
}
public override void Dispose() {
game.Keyboard.KeyRepeat = false;
base.Dispose();
}
protected void DisposeInput() {
if (input == null) return;
input = null;
for (int i = widgets.Length - 3; i < widgets.Length; i++) {
widgets[i].Dispose();
widgets[i] = null;
}
}
void OnOKButtonClick(Game game, Widget widget) { EnterInput(); }
protected void OnButtonClick(Game game, Widget widget) {
ButtonWidget button = (ButtonWidget)widget;
DisposeExtendedHelp();
int index = IndexOfWidget(button);
MenuInputValidator validator = validators[index];
activeButton = button;
DisposeInput();
input = MenuInputWidget.Create(game, 400, 30, button.GetValue(game), textFont, validator)
.SetLocation(Anchor.Centre, Anchor.Centre, 0, 110);
input.ShowCaret = true;
widgets[widgets.Length - 2] = input;
widgets[widgets.Length - 1] = ButtonWidget.Create(game, 40, "OK", titleFont, OnOKButtonClick)
.SetLocation(Anchor.Centre, Anchor.Centre, 240, 110);
widgets[widgets.Length - 3] = ButtonWidget.Create(game, 200, "Default value", titleFont, DefaultButtonClick)
.SetLocation(Anchor.Centre, Anchor.Centre, 0, 150);
UpdateDescription(activeButton);
}
void DefaultButtonClick(Game game, Widget widget) {
int index = IndexOfWidget(activeButton);
string defValue = defaultValues[index];
input.Clear();
input.Append(defValue);
}
void EnterInput() {
string text = input.Text.ToString();
if (((MenuInputWidget)input).Validator.IsValidValue(text)) {
SetButtonValue(activeButton, text);
}
UpdateDescription(activeButton);
activeButton = null;
DisposeInput();
}
protected override void ShowExtendedHelp() {
if (input == null) base.ShowExtendedHelp();
}
}
}

View File

@ -5,7 +5,7 @@ using ClassicalSharp.Gui.Widgets;
using ClassicalSharp.Singleplayer;
namespace ClassicalSharp.Gui.Screens {
public class MiscOptionsScreen : MenuOptionsScreen {
public class MiscOptionsScreen : ExtMenuOptionsScreen {
public MiscOptionsScreen(Game game) : base(game) {
}
@ -13,7 +13,17 @@ namespace ClassicalSharp.Gui.Screens {
public override void Init() {
base.Init();
ContextRecreated();
MakeValidators();
validators = new MenuInputValidator[widgets.Length];
defaultValues = new string[widgets.Length];
validators[0] = new RealValidator(1, 1024);
defaultValues[0] = "5";
validators[1] = new IntegerValidator(0, 100);
defaultValues[1] = "0";
validators[2] = new IntegerValidator(0, 100);
defaultValues[2] = "0";
validators[7] = new IntegerValidator(1, 200);
defaultValues[7] = "30";
}
protected override void ContextRecreated() {
@ -33,7 +43,7 @@ namespace ClassicalSharp.Gui.Screens {
MakeOpt(1, 50, "Mouse sensitivity", onClick, GetSensitivity, SetSensitivity),
MakeBack(false, titleFont, SwitchOptions),
null, null,
null, null, null,
};
}
@ -73,20 +83,5 @@ namespace ClassicalSharp.Gui.Screens {
g.MouseSensitivity = Int32.Parse(v);
Options.Set(OptionsKey.Sensitivity, v);
}
void MakeValidators() {
IServerConnection network = game.Server;
validators = new MenuInputValidator[] {
network.IsSinglePlayer ? new RealValidator(1, 1024) : null,
new IntegerValidator(0, 100),
new IntegerValidator(0, 100),
null,
null,
null,
null,
new IntegerValidator(1, 200),
};
}
}
}

View File

@ -32,7 +32,6 @@ namespace ClassicalSharp.Gui.Screens {
TextWidget.Create(game, "&eButtons on the right require restarting game", textFont)
.SetLocation(Anchor.Centre, Anchor.Centre, 0, 100),
MakeBack(false, titleFont, SwitchBack),
null, null,
};
}