Skip to content

Conversation

@pepoluan
Copy link

@pepoluan pepoluan commented Jun 1, 2021

Players have complained that sometimes the buttons to close a mod's
config (i.e., Cancel or "Save & Close") are outside the screen so they
can't click it. Pressing "Esc" or "E" also doesn't close the dialog.

It is because GMCM is still using Game1.viewport which has the gameworld
coordinates, which on players using gameworld zoom < UI zoom will cause
the coordinates of the menu elements to be outside the UI viewport,
hence not seen.

I brute-fixed this by replacing all instances of Game1.viewport with
Game1.uiViewport, and at the very least this bug is no fixed.

Also, I added some "null catching" code in RegisterModMenu() because
there had been spurious reports of GMCM not working well with
CustomMusic (something I cannot replicate on my machine)

Binaries built after this fix (i.e., version 1.3.4-unofficial.2-pepoluan) is available on SourceForge: https://sourceforge.net/projects/sdvmod-generic-mod-config-menu/files/

Players have complained that sometimes the buttons to close a mod's
config (i.e., Cancel or "Save & Close") are outside the screen so they
can't click it. Pressing "Esc" or "E" also doesn't close the dialog.

It is because GMCM is still using Game1.viewport which has the gameworld
coordinates, which on players using gameworld zoom < UI zoom will cause
the coordinates of the menu elements to be outside the UI viewport,
hence not seen.

I brute-fixed this by replacing all instances of Game1.viewport with
Game1.uiViewport, and at the very least this bug is no fixed.

Also, I added some "null catching" code in RegisterModMenu() because
there had been spurious reports of GMCM not working well with
CustomMusic (something I cannot replicate on my machine)

~pepoluan
Copy link
Author

@pepoluan pepoluan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comments about some of my decisions

Comment on lines +64 to +75
if (mod is null) {
apiLog("mod is null! Who called me??", LogLevel.Error);
throw new ArgumentNullException("mod");
}
if (revertToDefault is null) {
apiLog($"{mod.UniqueID} gave null revertToDefault!", LogLevel.Error);
throw new ArgumentNullException("revertToDefault");
}
if (saveToFile is null) {
apiLog($"{mod.UniqueID} gave null saveToFile!", LogLevel.Error);
throw new ArgumentNullException("saveToFile");
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is to help troubleshoot possible problems with other mods, namely CustomMusic which seems to, in certain situations, invoke this with nulls

Comment on lines +8 to +9
<Platforms>AnyCPU</Platforms>
<PlatformTarget>AnyCPU</PlatformTarget>
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Allow compatibility with SDV64

Comment on lines +84 to +154
//private void onLaunched(object sender, GameLaunchedEventArgs e)
//{
// config = Helper.ReadConfig<DummyConfig>();
// var api = Helper.ModRegistry.GetApi<IApi>(ModManifest.UniqueID);
// api.RegisterModConfig(ModManifest, () => config = new DummyConfig(), () => Helper.WriteConfig(config));
// api.SetDefaultIngameOptinValue( ModManifest, true );
// api.RegisterLabel(ModManifest, "Dummy Label", "Testing labels");
// api.RegisterParagraph( ModManifest, "Testing paragraph text. These are smaller than labels and should wrap based on length. In theory. They should also (in theory) support multiple rows. Whether that will look good or not is another question. But hey, it looks like it worked! Imagine that. Should I support images in documentation, too?" );
// api.RegisterImage( ModManifest, "Maps\\springobjects", new Rectangle( 32, 48, 16, 16 ) );
// api.RegisterImage( ModManifest, "Portraits\\Penny", null, 1 );
// api.SetDefaultIngameOptinValue( ModManifest, false );
// api.RegisterPageLabel( ModManifest, "Go to page: Simple Options", "", "Simple Options" );
// api.SetDefaultIngameOptinValue( ModManifest, true );
// api.RegisterPageLabel( ModManifest, "Go to page: Complex Options", "", "Complex Options" );
// api.SetDefaultIngameOptinValue( ModManifest, false );
// api.StartNewPage( ModManifest, "Simple Options" );
// api.RegisterPageLabel( ModManifest, "Back to main page", "", "" );
// api.RegisterSimpleOption(ModManifest, "Dummy Bool", "Testing a checkbox", () => config.dummyBool, (bool val) => config.dummyBool = val);
// api.RegisterSimpleOption(ModManifest, "Dummy Int (1)", "Testing an int (simple)", () => config.dummyInt1, (int val) => config.dummyInt1 = val);
// api.RegisterClampedOption(ModManifest, "Dummy Int (2)", "Testing an int (range)", () => config.dummyInt2, (int val) => config.dummyInt2 = val, 0, 100);
// api.RegisterSimpleOption(ModManifest, "Dummy Float (1)", "Testing a float (simple)", () => config.dummyFloat1, (float val) => config.dummyFloat1 = val);
// api.RegisterClampedOption(ModManifest, "Dummy Float (2)", "Testing a float (range)", () => config.dummyFloat2, (float val) => config.dummyFloat2 = val, 0, 1);
// api.SetDefaultIngameOptinValue( ModManifest, true );
// api.StartNewPage( ModManifest, "Complex Options" );
// api.RegisterPageLabel( ModManifest, "Back to main page", "", "" );
// api.RegisterSimpleOption(ModManifest, "Dummy String (1)", "Testing a string", () => config.dummyString1, (string val) => config.dummyString1 = val);
// api.RegisterChoiceOption(ModManifest, "Dummy String (2)", "Testing a dropdown box", () => config.dummyString2, (string val) => config.dummyString2 = val, DummyConfig.dummyString2Choices);
// api.RegisterSimpleOption(ModManifest, "Dummy Keybinding", "Testing a keybinding", () => config.dummyKeybinding, (SButton val) => config.dummyKeybinding = val);
// api.RegisterSimpleOption(ModManifest, "Dummy Keybinding 2", "Testing a keybinding list", () => config.dummyKeybinding2, (KeybindList val) => config.dummyKeybinding2 = val);

// api.RegisterLabel(ModManifest, "", "");

// // Complex widget - this just generates a random color on click.
// Func<Vector2, object, object> randomColorUpdate =
// (Vector2 pos, object state_) =>
// {
// var state = state_ as RandomColorWidgetState;
// if (state == null)
// state = new RandomColorWidgetState() { color = config.dummyColor };

// var bounds = new Rectangle((int)pos.X + 12, (int)pos.Y + 12, 50 - 12 * 2, 50 - 12 * 2);
// bool hover = bounds.Contains(Game1.getOldMouseX(), Game1.getOldMouseY());
// if ( hover && Game1.oldMouseState.LeftButton == ButtonState.Released && Mouse.GetState().LeftButton == ButtonState.Pressed)
// {
// Game1.playSound("drumkit6");
// Random r = new Random();
// state.color.R = (byte) r.Next(256);
// state.color.G = (byte) r.Next(256);
// state.color.B = (byte) r.Next(256);
// }

// return state;
// };
// Func<SpriteBatch, Vector2, object, object> randomColorDraw =
// (SpriteBatch b, Vector2 pos, object state_) =>
// {
// var state = state_ as RandomColorWidgetState;
// IClickableMenu.drawTextureBox(b, (int)pos.X, (int)pos.Y, 50, 50, Color.White);
// var colorBox = new Rectangle((int)pos.X + 12, (int)pos.Y + 12, 50 - 12 * 2, 50 - 12 * 2);
// b.Draw(Game1.staminaRect, colorBox, state.color);
// return state;
// };
// Action<object> randomColorSave =
// (object state) =>
// {
// if ( state == null )
// return;
// config.dummyColor = (state as RandomColorWidgetState).color;
// };
// api.RegisterComplexOption(ModManifest, "Dummy Color", "Testing a complex widget (random color on click)", randomColorUpdate, randomColorDraw, randomColorSave);
//}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented out this whole func (and remove ref to it in Entry) because it's just a sample.

* Position of config button on Title Screen
* Suppression of Exit Button for non-Android
Comment on lines -35 to +44
configButton = new Button(tex);
configButton.LocalPosition = new Vector2(36, Game1.viewport.Height - 100);
configButton.Callback = (Element e) =>
{
Game1.playSound("newArtifact");
TitleMenu.subMenu = new ModConfigMenu( false );
};
configButton = new Button(tex) {
LocalPosition = new Vector2(36, Game1.viewport.Height - 100),
Callback =
(Element e) => {
Game1.playSound("newArtifact");
TitleMenu.subMenu = new ModConfigMenu(false);
}
};
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Originally changed this to uiViewport as well, but apparently Title Screen uses viewport instead.

So, I reverted my change, but kept the "new-style" object property initialization syntax.

Comment on lines -49 to +52
if (ingame || Constants.TargetPlatform == GamePlatform.Android)
if (Constants.TargetPlatform == GamePlatform.Android)
initializeUpperRightCloseButton();
else
upperRightCloseButton = null;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I failed to fix the "half-visible" close button, so I just suppress it altogether on non-Android.


if (Constants.TargetPlatform != GamePlatform.Android)
{
Mod.instance.Helper.Events.Input.ButtonReleased += CheckEscape;
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This addition allows user to exit from "Specific Mod Config" menu using the Escape button.

Comment on lines +474 to +480
private void CheckEscape(object sender, ButtonReleasedEventArgs args) {
if (DoingKeyBinding) return;
if (args.Button.ToString() == "Escape") {
Mod.instance.Helper.Events.Input.ButtonReleased -= CheckEscape;
cancel();
}
}
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This handles the event of user pressing Escape while not doing key binding. It is hardcoded to act similarly to pressing the "Cancel" button.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant