Initial commit: OpenRA game engine
Some checks failed
Continuous Integration / Linux (.NET 8.0) (push) Has been cancelled
Continuous Integration / Windows (.NET 8.0) (push) Has been cancelled

Fork from OpenRA/OpenRA with one-click launch script (start-ra.cmd)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
let5sne.win10
2026-01-10 21:46:54 +08:00
commit 9cf6ebb986
4065 changed files with 635973 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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 OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets.Logic
{
public class AdvancedSettingsLogic : ChromeLogic
{
readonly DebugSettings debugSettings;
readonly GameSettings gameSettings;
readonly ServerSettings serverSettings;
static ServerSettings originalServerSettings;
[ObjectCreator.UseCtor]
public AdvancedSettingsLogic(ModData modData, SettingsLogic settingsLogic, string panelID, string label)
{
debugSettings = modData.GetSettings<DebugSettings>();
gameSettings = modData.GetSettings<GameSettings>();
serverSettings = modData.GetSettings<ServerSettings>();
originalServerSettings ??= serverSettings.Clone();
settingsLogic.RegisterSettingsPanel(panelID, label, InitPanel, ResetPanel);
}
Func<bool> InitPanel(Widget panel)
{
var scrollPanel = panel.Get<ScrollPanelWidget>("SETTINGS_SCROLLPANEL");
// Advanced
SettingsUtils.BindCheckboxPref(panel, "NAT_DISCOVERY", serverSettings, "DiscoverNatDevices");
SettingsUtils.BindCheckboxPref(panel, "PERFTEXT_CHECKBOX", debugSettings, "PerfText");
SettingsUtils.BindCheckboxPref(panel, "PERFGRAPH_CHECKBOX", debugSettings, "PerfGraph");
SettingsUtils.BindCheckboxPref(panel, "FETCH_NEWS_CHECKBOX", gameSettings, "FetchNews");
SettingsUtils.BindCheckboxPref(panel, "SENDSYSINFO_CHECKBOX", debugSettings, "SendSystemInformation");
SettingsUtils.BindCheckboxPref(panel, "CHECK_VERSION_CHECKBOX", debugSettings, "CheckVersion");
var ssi = panel.Get<CheckboxWidget>("SENDSYSINFO_CHECKBOX");
ssi.IsDisabled = () => !gameSettings.FetchNews;
// Developer
SettingsUtils.BindCheckboxPref(panel, "BOTDEBUG_CHECKBOX", debugSettings, "BotDebug");
SettingsUtils.BindCheckboxPref(panel, "LUADEBUG_CHECKBOX", debugSettings, "LuaDebug");
SettingsUtils.BindCheckboxPref(panel, "REPLAY_COMMANDS_CHECKBOX", debugSettings, "EnableDebugCommandsInReplays");
SettingsUtils.BindCheckboxPref(panel, "CHECKUNSYNCED_CHECKBOX", debugSettings, "SyncCheckUnsyncedCode");
SettingsUtils.BindCheckboxPref(panel, "CHECKBOTSYNC_CHECKBOX", debugSettings, "SyncCheckBotModuleCode");
SettingsUtils.BindCheckboxPref(panel, "PERFLOGGING_CHECKBOX", debugSettings, "EnableSimulationPerfLogging");
panel.Get("BOTDEBUG_CHECKBOX_CONTAINER").IsVisible = () => debugSettings.DisplayDeveloperSettings;
panel.Get("CHECKUNSYNCED_CHECKBOX_CONTAINER").IsVisible = () => debugSettings.DisplayDeveloperSettings;
panel.Get("CHECKBOTSYNC_CHECKBOX_CONTAINER").IsVisible = () => debugSettings.DisplayDeveloperSettings;
panel.Get("LUADEBUG_CHECKBOX_CONTAINER").IsVisible = () => debugSettings.DisplayDeveloperSettings;
panel.Get("REPLAY_COMMANDS_CHECKBOX_CONTAINER").IsVisible = () => debugSettings.DisplayDeveloperSettings;
panel.Get("PERFLOGGING_CHECKBOX_CONTAINER").IsVisible = () => debugSettings.DisplayDeveloperSettings;
panel.Get("DEBUG_HIDDEN_CONTAINER").IsVisible = () => !debugSettings.DisplayDeveloperSettings;
SettingsUtils.AdjustSettingsScrollPanelLayout(scrollPanel);
return () => serverSettings.DiscoverNatDevices != originalServerSettings.DiscoverNatDevices;
}
Action ResetPanel(Widget panel)
{
var defaultDebugSettings = new DebugSettings();
var defaultServerSettings = new ServerSettings();
return () =>
{
serverSettings.DiscoverNatDevices = defaultServerSettings.DiscoverNatDevices;
debugSettings.PerfText = defaultDebugSettings.PerfText;
debugSettings.PerfGraph = defaultDebugSettings.PerfGraph;
debugSettings.SyncCheckUnsyncedCode = defaultDebugSettings.SyncCheckUnsyncedCode;
debugSettings.SyncCheckBotModuleCode = defaultDebugSettings.SyncCheckBotModuleCode;
debugSettings.BotDebug = defaultDebugSettings.BotDebug;
debugSettings.LuaDebug = defaultDebugSettings.LuaDebug;
debugSettings.SendSystemInformation = defaultDebugSettings.SendSystemInformation;
debugSettings.CheckVersion = defaultDebugSettings.CheckVersion;
debugSettings.EnableDebugCommandsInReplays = defaultDebugSettings.EnableDebugCommandsInReplays;
debugSettings.EnableSimulationPerfLogging = defaultDebugSettings.EnableSimulationPerfLogging;
};
}
}
}

View File

@@ -0,0 +1,173 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Linq;
using OpenRA.Graphics;
using OpenRA.Mods.Common.Traits;
using OpenRA.Primitives;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets.Logic
{
public class AudioSettingsLogic : ChromeLogic
{
readonly WorldRenderer worldRenderer;
readonly SoundSettings soundSettings;
static SoundSettings originalSoundSettings;
SoundDevice soundDevice;
[ObjectCreator.UseCtor]
public AudioSettingsLogic(ModData modData, SettingsLogic settingsLogic, string panelID, string label, WorldRenderer worldRenderer)
{
this.worldRenderer = worldRenderer;
soundSettings = modData.GetSettings<SoundSettings>();
originalSoundSettings ??= soundSettings.Clone();
settingsLogic.RegisterSettingsPanel(panelID, label, InitPanel, ResetPanel);
}
Func<bool> InitPanel(Widget panel)
{
var musicPlaylist = worldRenderer.World.WorldActor.Trait<MusicPlaylist>();
var scrollPanel = panel.Get<ScrollPanelWidget>("SETTINGS_SCROLLPANEL");
SettingsUtils.BindCheckboxPref(panel, "CASH_TICKS", soundSettings, "CashTicks");
SettingsUtils.BindCheckboxPref(panel, "MUTE_SOUND", soundSettings, "Mute");
SettingsUtils.BindCheckboxPref(panel, "MUTE_BACKGROUND_MUSIC", soundSettings, "MuteBackgroundMusic");
SettingsUtils.BindSliderPref(panel, "SOUND_VOLUME", soundSettings, "SoundVolume");
SettingsUtils.BindSliderPref(panel, "MUSIC_VOLUME", soundSettings, "MusicVolume");
SettingsUtils.BindSliderPref(panel, "VIDEO_VOLUME", soundSettings, "VideoVolume");
var muteCheckbox = panel.Get<CheckboxWidget>("MUTE_SOUND");
var muteCheckboxOnClick = muteCheckbox.OnClick;
var muteCheckboxIsChecked = muteCheckbox.IsChecked;
muteCheckbox.IsChecked = () => muteCheckboxIsChecked() || Game.Sound.DummyEngine;
muteCheckbox.IsDisabled = () => Game.Sound.DummyEngine;
muteCheckbox.OnClick = () =>
{
muteCheckboxOnClick();
if (soundSettings.Mute)
Game.Sound.MuteAudio();
else
Game.Sound.UnmuteAudio();
};
var muteBackgroundMusicCheckbox = panel.Get<CheckboxWidget>("MUTE_BACKGROUND_MUSIC");
var muteBackgroundMusicCheckboxOnClick = muteBackgroundMusicCheckbox.OnClick;
muteBackgroundMusicCheckbox.OnClick = () =>
{
muteBackgroundMusicCheckboxOnClick();
if (!musicPlaylist.AllowMuteBackgroundMusic)
return;
if (musicPlaylist.CurrentSongIsBackground)
musicPlaylist.Stop();
};
// Replace controls with a warning label if sound is disabled
var noDeviceLabel = panel.GetOrNull("NO_AUDIO_DEVICE_CONTAINER");
if (noDeviceLabel != null)
noDeviceLabel.Visible = Game.Sound.DummyEngine;
panel.Get("CASH_TICKS_CONTAINER").Visible = !Game.Sound.DummyEngine;
panel.Get("MUTE_SOUND_CONTAINER").Visible = !Game.Sound.DummyEngine;
panel.Get("MUTE_BACKGROUND_MUSIC_CONTAINER").Visible = !Game.Sound.DummyEngine;
panel.Get("SOUND_VOLUME_CONTAINER").Visible = !Game.Sound.DummyEngine;
panel.Get("MUSIC_VOLUME_CONTAINER").Visible = !Game.Sound.DummyEngine;
panel.Get("VIDEO_VOLUME_CONTAINER").Visible = !Game.Sound.DummyEngine;
var soundVolumeSlider = panel.Get<SliderWidget>("SOUND_VOLUME");
soundVolumeSlider.OnChange += x => Game.Sound.SoundVolume = x;
var musicVolumeSlider = panel.Get<SliderWidget>("MUSIC_VOLUME");
musicVolumeSlider.OnChange += x => Game.Sound.MusicVolume = x;
var videoVolumeSlider = panel.Get<SliderWidget>("VIDEO_VOLUME");
videoVolumeSlider.OnChange += x => Game.Sound.VideoVolume = x;
var devices = Game.Sound.AvailableDevices();
soundDevice = devices.FirstOrDefault(d => d.Device == soundSettings.Device) ?? devices[0];
var audioDeviceDropdown = panel.Get<DropDownButtonWidget>("AUDIO_DEVICE");
audioDeviceDropdown.OnMouseDown = _ => ShowAudioDeviceDropdown(audioDeviceDropdown, devices, scrollPanel);
var deviceFont = Game.Renderer.Fonts[audioDeviceDropdown.Font];
var deviceLabel = new CachedTransform<SoundDevice, string>(
s => WidgetUtils.TruncateText(s.Label, audioDeviceDropdown.UsableWidth, deviceFont));
audioDeviceDropdown.GetText = () => deviceLabel.Update(soundDevice);
var restartDesc = panel.Get("AUDIO_RESTART_REQUIRED_DESC");
restartDesc.IsVisible = () => soundDevice.Device != originalSoundSettings.Device;
SettingsUtils.AdjustSettingsScrollPanelLayout(scrollPanel);
return () =>
{
soundSettings.Device = soundDevice.Device;
return soundSettings.Device != originalSoundSettings.Device;
};
}
Action ResetPanel(Widget panel)
{
var defaultSoundSettings = new SoundSettings();
return () =>
{
soundSettings.SoundVolume = defaultSoundSettings.SoundVolume;
soundSettings.MusicVolume = defaultSoundSettings.MusicVolume;
soundSettings.VideoVolume = defaultSoundSettings.VideoVolume;
soundSettings.CashTicks = defaultSoundSettings.CashTicks;
soundSettings.Mute = defaultSoundSettings.Mute;
soundSettings.MuteBackgroundMusic = defaultSoundSettings.MuteBackgroundMusic;
soundSettings.Device = defaultSoundSettings.Device;
panel.Get<SliderWidget>("SOUND_VOLUME").Value = soundSettings.SoundVolume;
Game.Sound.SoundVolume = soundSettings.SoundVolume;
panel.Get<SliderWidget>("MUSIC_VOLUME").Value = soundSettings.MusicVolume;
Game.Sound.MusicVolume = soundSettings.MusicVolume;
panel.Get<SliderWidget>("VIDEO_VOLUME").Value = soundSettings.VideoVolume;
Game.Sound.VideoVolume = soundSettings.VideoVolume;
Game.Sound.UnmuteAudio();
soundDevice = Game.Sound.AvailableDevices().First();
};
}
void ShowAudioDeviceDropdown(DropDownButtonWidget dropdown, SoundDevice[] devices, ScrollPanelWidget scrollPanel)
{
var i = 0;
var options = devices.ToDictionary(d => i++.ToStringInvariant(), d => d);
ScrollItemWidget SetupItem(string o, ScrollItemWidget itemTemplate)
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => soundDevice == options[o],
() =>
{
soundDevice = options[o];
SettingsUtils.AdjustSettingsScrollPanelLayout(scrollPanel);
});
var deviceLabel = item.Get<LabelWidget>("LABEL");
var font = Game.Renderer.Fonts[deviceLabel.Font];
var label = WidgetUtils.TruncateText(options[o].Label, deviceLabel.Bounds.Width, font);
deviceLabel.GetText = () => label;
return item;
}
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, options.Keys, SetupItem);
}
}
}

View File

@@ -0,0 +1,512 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using OpenRA.Graphics;
using OpenRA.Primitives;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets.Logic
{
public class DisplaySettingsLogic : ChromeLogic
{
[FluentReference]
const string Close = "options-camera.close";
[FluentReference]
const string Medium = "options-camera.medium";
[FluentReference]
const string Far = "options-camera.far";
[FluentReference]
const string Furthest = "options-camera.furthest";
[FluentReference]
const string Windowed = "options-display-mode.windowed";
[FluentReference]
const string LegacyFullscreen = "options-display-mode.legacy-fullscreen";
[FluentReference]
const string Fullscreen = "options-display-mode.fullscreen";
[FluentReference("number")]
const string Display = "label-video-display-index";
[FluentReference]
const string Standard = "options-status-bars.standard";
[FluentReference]
const string ShowOnDamage = "options-status-bars.show-on-damage";
[FluentReference]
const string AlwaysShow = "options-status-bars.always-show";
[FluentReference]
const string Automatic = "options-target-lines.automatic";
[FluentReference]
const string Manual = "options-target-lines.manual";
[FluentReference]
const string Disabled = "options-target-lines.disabled";
[FluentReference("fps")]
const string FrameLimiter = "checkbox-frame-limiter";
readonly ModData modData;
readonly WorldRenderer worldRenderer;
readonly WorldViewportSizes viewportSizes;
readonly GameSettings gameSettings;
readonly GraphicSettings graphicSettings;
static GraphicSettings originalGraphicSettings;
readonly string showOnDamage;
readonly string alwaysShow;
readonly string automatic;
readonly string manual;
readonly string disabled;
readonly string legacyFullscreen;
readonly string fullscreen;
[ObjectCreator.UseCtor]
public DisplaySettingsLogic(ModData modData, SettingsLogic settingsLogic, string panelID, string label, WorldRenderer worldRenderer)
{
this.worldRenderer = worldRenderer;
this.modData = modData;
viewportSizes = modData.GetOrCreate<WorldViewportSizes>();
gameSettings = modData.GetSettings<GameSettings>();
graphicSettings = modData.GetSettings<GraphicSettings>();
originalGraphicSettings ??= graphicSettings.Clone();
legacyFullscreen = FluentProvider.GetMessage(LegacyFullscreen);
fullscreen = FluentProvider.GetMessage(Fullscreen);
settingsLogic.RegisterSettingsPanel(panelID, label, InitPanel, ResetPanel);
showOnDamage = FluentProvider.GetMessage(ShowOnDamage);
alwaysShow = FluentProvider.GetMessage(AlwaysShow);
automatic = FluentProvider.GetMessage(Automatic);
manual = FluentProvider.GetMessage(Manual);
disabled = FluentProvider.GetMessage(Disabled);
}
public static string GetViewportSizeName(ModData modData, WorldViewport worldViewport)
{
switch (worldViewport)
{
case WorldViewport.Close:
return FluentProvider.GetMessage(Close);
case WorldViewport.Medium:
return FluentProvider.GetMessage(Medium);
case WorldViewport.Far:
return FluentProvider.GetMessage(Far);
case WorldViewport.Native:
return FluentProvider.GetMessage(Furthest);
default:
return "";
}
}
Func<bool> InitPanel(Widget panel)
{
var world = worldRenderer.World;
var scrollPanel = panel.Get<ScrollPanelWidget>("SETTINGS_SCROLLPANEL");
SettingsUtils.BindCheckboxPref(panel, "CURSORDOUBLE_CHECKBOX", graphicSettings, "CursorDouble");
SettingsUtils.BindCheckboxPref(panel, "VSYNC_CHECKBOX", graphicSettings, "VSync");
SettingsUtils.BindCheckboxPref(panel, "FRAME_LIMIT_CHECKBOX", graphicSettings, "CapFramerate");
SettingsUtils.BindCheckboxPref(panel, "FRAME_LIMIT_GAMESPEED_CHECKBOX", graphicSettings, "CapFramerateToGameFps");
SettingsUtils.BindIntSliderPref(panel, "FRAME_LIMIT_SLIDER", graphicSettings, "MaxFramerate");
SettingsUtils.BindCheckboxPref(panel, "PLAYER_STANCE_COLORS_CHECKBOX", gameSettings, "UsePlayerStanceColors");
var cb = panel.Get<CheckboxWidget>("PLAYER_STANCE_COLORS_CHECKBOX");
cb.IsChecked = () => gameSettings.UsePlayerStanceColors;
cb.OnClick = () =>
{
gameSettings.UsePlayerStanceColors = cb.IsChecked() ^ true;
Player.SetupRelationshipColors(world.Players, world.LocalPlayer, worldRenderer, false);
};
if (panel.GetOrNull<CheckboxWidget>("PAUSE_SHELLMAP_CHECKBOX") != null)
SettingsUtils.BindCheckboxPref(panel, "PAUSE_SHELLMAP_CHECKBOX", gameSettings, "PauseShellmap");
var windowModeDropdown = panel.Get<DropDownButtonWidget>("MODE_DROPDOWN");
windowModeDropdown.OnMouseDown = _ => ShowWindowModeDropdown(windowModeDropdown, graphicSettings, scrollPanel);
windowModeDropdown.GetText = () => graphicSettings.Mode == WindowMode.Windowed
? FluentProvider.GetMessage(Windowed)
: graphicSettings.Mode == WindowMode.Fullscreen ? legacyFullscreen : fullscreen;
var displaySelectionDropDown = panel.Get<DropDownButtonWidget>("DISPLAY_SELECTION_DROPDOWN");
displaySelectionDropDown.OnMouseDown = _ => ShowDisplaySelectionDropdown(displaySelectionDropDown, graphicSettings);
var displaySelectionLabel = new CachedTransform<int, string>(i => FluentProvider.GetMessage(Display, "number", i + 1));
displaySelectionDropDown.GetText = () => displaySelectionLabel.Update(graphicSettings.VideoDisplay);
displaySelectionDropDown.IsDisabled = () => Game.Renderer.DisplayCount < 2;
var glProfileLabel = new CachedTransform<GLProfile, string>(p => p.ToString());
var glProfileDropdown = panel.Get<DropDownButtonWidget>("GL_PROFILE_DROPDOWN");
var disableProfile = Game.Renderer.SupportedGLProfiles.Length < 2 && graphicSettings.GLProfile == GLProfile.Automatic;
glProfileDropdown.OnMouseDown = _ => ShowGLProfileDropdown(glProfileDropdown, graphicSettings);
glProfileDropdown.GetText = () => glProfileLabel.Update(graphicSettings.GLProfile);
glProfileDropdown.IsDisabled = () => disableProfile;
var statusBarsDropDown = panel.Get<DropDownButtonWidget>("STATUS_BAR_DROPDOWN");
statusBarsDropDown.OnMouseDown = _ => ShowStatusBarsDropdown(statusBarsDropDown, gameSettings);
statusBarsDropDown.GetText = () => gameSettings.StatusBars == StatusBarsType.Standard
? FluentProvider.GetMessage(Standard)
: gameSettings.StatusBars == StatusBarsType.DamageShow
? showOnDamage
: alwaysShow;
var targetLinesDropDown = panel.Get<DropDownButtonWidget>("TARGET_LINES_DROPDOWN");
targetLinesDropDown.OnMouseDown = _ => ShowTargetLinesDropdown(targetLinesDropDown, gameSettings);
targetLinesDropDown.GetText = () => gameSettings.TargetLines == TargetLinesType.Automatic
? automatic
: gameSettings.TargetLines == TargetLinesType.Manual
? manual
: disabled;
var battlefieldCameraDropDown = panel.Get<DropDownButtonWidget>("BATTLEFIELD_CAMERA_DROPDOWN");
var battlefieldCameraLabel = new CachedTransform<WorldViewport, string>(vs => GetViewportSizeName(modData, vs));
battlefieldCameraDropDown.OnMouseDown = _ => ShowBattlefieldCameraDropdown(modData, battlefieldCameraDropDown, viewportSizes, graphicSettings);
battlefieldCameraDropDown.GetText = () => battlefieldCameraLabel.Update(graphicSettings.ViewportDistance);
BindTextNotificationPoolFilterSettings(panel, gameSettings);
// Update vsync immediately
var vsyncCheckbox = panel.Get<CheckboxWidget>("VSYNC_CHECKBOX");
var vsyncOnClick = vsyncCheckbox.OnClick;
vsyncCheckbox.OnClick = () =>
{
vsyncOnClick();
Game.Renderer.SetVSyncEnabled(graphicSettings.VSync);
};
var uiScaleDropdown = panel.Get<DropDownButtonWidget>("UI_SCALE_DROPDOWN");
var uiScaleLabel = new CachedTransform<float, string>(s => $"{(int)(100 * s)}%");
uiScaleDropdown.OnMouseDown = _ => ShowUIScaleDropdown(uiScaleDropdown, graphicSettings);
uiScaleDropdown.GetText = () => uiScaleLabel.Update(graphicSettings.UIScale);
var minResolution = viewportSizes.MinEffectiveResolution;
var resolution = Game.Renderer.Resolution;
var disableUIScale = world.Type != WorldType.Shellmap ||
resolution.Width * graphicSettings.UIScale < 1.25f * minResolution.Width ||
resolution.Height * graphicSettings.UIScale < 1.25f * minResolution.Height;
uiScaleDropdown.IsDisabled = () => disableUIScale;
panel.Get("DISPLAY_SELECTION_CONTAINER").IsVisible = () => graphicSettings.Mode != WindowMode.Windowed;
panel.Get("WINDOW_RESOLUTION_CONTAINER").IsVisible = () => graphicSettings.Mode == WindowMode.Windowed;
var windowWidth = panel.Get<TextFieldWidget>("WINDOW_WIDTH");
var origWidthText = windowWidth.Text = graphicSettings.WindowedSize.X.ToString(NumberFormatInfo.CurrentInfo);
var windowHeight = panel.Get<TextFieldWidget>("WINDOW_HEIGHT");
var origHeightText = windowHeight.Text = graphicSettings.WindowedSize.Y.ToString(NumberFormatInfo.CurrentInfo);
windowHeight.Text = graphicSettings.WindowedSize.Y.ToString(NumberFormatInfo.CurrentInfo);
var restartDesc = panel.Get("VIDEO_RESTART_REQUIRED_DESC");
restartDesc.IsVisible = () => graphicSettings.Mode != originalGraphicSettings.Mode ||
graphicSettings.VideoDisplay != originalGraphicSettings.VideoDisplay ||
graphicSettings.GLProfile != originalGraphicSettings.GLProfile ||
(graphicSettings.Mode == WindowMode.Windowed && (origWidthText != windowWidth.Text || origHeightText != windowHeight.Text));
var frameLimitGamespeedCheckbox = panel.Get<CheckboxWidget>("FRAME_LIMIT_GAMESPEED_CHECKBOX");
var frameLimitCheckbox = panel.Get<CheckboxWidget>("FRAME_LIMIT_CHECKBOX");
var frameLimitLabel = new CachedTransform<int, string>(fps => FluentProvider.GetMessage(FrameLimiter, "fps", fps));
frameLimitCheckbox.GetText = () => frameLimitLabel.Update(graphicSettings.MaxFramerate);
frameLimitCheckbox.IsDisabled = () => graphicSettings.CapFramerateToGameFps;
panel.Get<SliderWidget>("FRAME_LIMIT_SLIDER").IsDisabled = () => !frameLimitCheckbox.IsChecked() || frameLimitGamespeedCheckbox.IsChecked();
SettingsUtils.AdjustSettingsScrollPanelLayout(scrollPanel);
return () =>
{
int.TryParse(windowWidth.Text, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out var x);
int.TryParse(windowHeight.Text, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out var y);
graphicSettings.WindowedSize = new int2(x, y);
return graphicSettings.Mode != originalGraphicSettings.Mode ||
graphicSettings.VideoDisplay != originalGraphicSettings.VideoDisplay ||
graphicSettings.WindowedSize != originalGraphicSettings.WindowedSize ||
graphicSettings.FullscreenSize != originalGraphicSettings.FullscreenSize ||
graphicSettings.GLProfile != originalGraphicSettings.GLProfile;
};
}
Action ResetPanel(Widget panel)
{
var defaultGameSettings = new GameSettings();
var defaultGraphicSettings = new GraphicSettings();
return () =>
{
graphicSettings.CapFramerate = defaultGraphicSettings.CapFramerate;
graphicSettings.MaxFramerate = defaultGraphicSettings.MaxFramerate;
graphicSettings.CapFramerateToGameFps = defaultGraphicSettings.CapFramerateToGameFps;
graphicSettings.GLProfile = defaultGraphicSettings.GLProfile;
graphicSettings.Mode = defaultGraphicSettings.Mode;
graphicSettings.VideoDisplay = defaultGraphicSettings.VideoDisplay;
graphicSettings.WindowedSize = defaultGraphicSettings.WindowedSize;
graphicSettings.CursorDouble = defaultGraphicSettings.CursorDouble;
graphicSettings.ViewportDistance = defaultGraphicSettings.ViewportDistance;
if (graphicSettings.UIScale != defaultGraphicSettings.UIScale)
{
var oldScale = graphicSettings.UIScale;
graphicSettings.UIScale = defaultGraphicSettings.UIScale;
Game.Renderer.SetUIScale(defaultGraphicSettings.UIScale);
RecalculateWidgetLayout(Ui.Root);
Viewport.LastMousePos = (Viewport.LastMousePos.ToFloat2() * oldScale / graphicSettings.UIScale).ToInt2();
}
gameSettings.TextNotificationPoolFilters = defaultGameSettings.TextNotificationPoolFilters;
};
}
static void ShowWindowModeDropdown(DropDownButtonWidget dropdown, GraphicSettings graphicSettings, ScrollPanelWidget scrollPanel)
{
var options = new Dictionary<string, WindowMode>()
{
{ FluentProvider.GetMessage(Fullscreen), WindowMode.PseudoFullscreen },
{ FluentProvider.GetMessage(LegacyFullscreen), WindowMode.Fullscreen },
{ FluentProvider.GetMessage(Windowed), WindowMode.Windowed },
};
ScrollItemWidget SetupItem(string o, ScrollItemWidget itemTemplate)
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => graphicSettings.Mode == options[o],
() =>
{
graphicSettings.Mode = options[o];
SettingsUtils.AdjustSettingsScrollPanelLayout(scrollPanel);
});
item.Get<LabelWidget>("LABEL").GetText = () => o;
return item;
}
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, options.Keys, SetupItem);
}
public static void BindTextNotificationPoolFilterSettings(Widget panel, GameSettings gameSettings)
{
void ToggleFilterFlag(TextNotificationPoolFilters f)
{
gameSettings.TextNotificationPoolFilters ^= f;
gameSettings.Save();
}
var feedbackCheckbox = panel.GetOrNull<CheckboxWidget>("UI_FEEDBACK_CHECKBOX");
if (feedbackCheckbox != null)
{
feedbackCheckbox.IsChecked = () => gameSettings.TextNotificationPoolFilters.HasFlag(TextNotificationPoolFilters.Feedback);
feedbackCheckbox.OnClick = () => ToggleFilterFlag(TextNotificationPoolFilters.Feedback);
}
var transientsCheckbox = panel.GetOrNull<CheckboxWidget>("TRANSIENTS_CHECKBOX");
if (transientsCheckbox != null)
{
transientsCheckbox.IsChecked = () => gameSettings.TextNotificationPoolFilters.HasFlag(TextNotificationPoolFilters.Transients);
transientsCheckbox.OnClick = () => ToggleFilterFlag(TextNotificationPoolFilters.Transients);
}
}
static void ShowStatusBarsDropdown(DropDownButtonWidget dropdown, GameSettings gameSettings)
{
var options = new Dictionary<string, StatusBarsType>()
{
{ FluentProvider.GetMessage(Standard), StatusBarsType.Standard },
{ FluentProvider.GetMessage(ShowOnDamage), StatusBarsType.DamageShow },
{ FluentProvider.GetMessage(AlwaysShow), StatusBarsType.AlwaysShow },
};
ScrollItemWidget SetupItem(string o, ScrollItemWidget itemTemplate)
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => gameSettings.StatusBars == options[o],
() => gameSettings.StatusBars = options[o]);
item.Get<LabelWidget>("LABEL").GetText = () => o;
return item;
}
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, options.Keys, SetupItem);
}
static void ShowDisplaySelectionDropdown(DropDownButtonWidget dropdown, GraphicSettings graphicSettings)
{
ScrollItemWidget SetupItem(int o, ScrollItemWidget itemTemplate)
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => graphicSettings.VideoDisplay == o,
() => graphicSettings.VideoDisplay = o);
var label = $"Display {o + 1}";
item.Get<LabelWidget>("LABEL").GetText = () => label;
return item;
}
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, Enumerable.Range(0, Game.Renderer.DisplayCount), SetupItem);
}
static void ShowGLProfileDropdown(DropDownButtonWidget dropdown, GraphicSettings graphicSettings)
{
ScrollItemWidget SetupItem(GLProfile o, ScrollItemWidget itemTemplate)
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => graphicSettings.GLProfile == o,
() => graphicSettings.GLProfile = o);
var label = o.ToString();
item.Get<LabelWidget>("LABEL").GetText = () => label;
return item;
}
var profiles = new[] { GLProfile.Automatic }.Concat(Game.Renderer.SupportedGLProfiles);
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, profiles, SetupItem);
}
static void ShowTargetLinesDropdown(DropDownButtonWidget dropdown, GameSettings gameSettings)
{
var options = new Dictionary<string, TargetLinesType>()
{
{ FluentProvider.GetMessage(Automatic), TargetLinesType.Automatic },
{ FluentProvider.GetMessage(Manual), TargetLinesType.Manual },
{ FluentProvider.GetMessage(Disabled), TargetLinesType.Disabled },
};
ScrollItemWidget SetupItem(string o, ScrollItemWidget itemTemplate)
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => gameSettings.TargetLines == options[o],
() => gameSettings.TargetLines = options[o]);
item.Get<LabelWidget>("LABEL").GetText = () => o;
return item;
}
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, options.Keys, SetupItem);
}
public static void ShowBattlefieldCameraDropdown(ModData modData, DropDownButtonWidget dropdown,
WorldViewportSizes viewportSizes, GraphicSettings graphicSettings)
{
ScrollItemWidget SetupItem(WorldViewport o, ScrollItemWidget itemTemplate)
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => graphicSettings.ViewportDistance == o,
() => graphicSettings.ViewportDistance = o);
var label = GetViewportSizeName(modData, o);
item.Get<LabelWidget>("LABEL").GetText = () => label;
return item;
}
var windowHeight = Game.Renderer.NativeResolution.Height;
var validSizes = new List<WorldViewport>() { WorldViewport.Close };
if (viewportSizes.GetSizeRange(WorldViewport.Medium).X < windowHeight)
validSizes.Add(WorldViewport.Medium);
var farRange = viewportSizes.GetSizeRange(WorldViewport.Far);
if (farRange.X < windowHeight)
validSizes.Add(WorldViewport.Far);
if (viewportSizes.AllowNativeZoom && farRange.Y < windowHeight)
validSizes.Add(WorldViewport.Native);
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, validSizes, SetupItem);
}
static void RecalculateWidgetLayout(Widget w, bool insideScrollPanel = false)
{
// HACK: Recalculate the widget bounds to fit within the new effective window bounds
// This is fragile, and only works when called when Settings is opened via the main menu.
// HACK: Skip children badges container on the main menu and settings tab container
// These have a fixed size, with calculated size and children positions that break if we adjust them here
if (w.Id == "BADGES_CONTAINER" || w.Id == "SETTINGS_TAB_CONTAINER")
return;
var parentBounds = w.Parent == null
? new WidgetBounds(0, 0, Game.Renderer.Resolution.Width, Game.Renderer.Resolution.Height)
: w.Parent.Bounds;
var substitutions = new Dictionary<string, int>
{
{ "WINDOW_WIDTH", Game.Renderer.Resolution.Width },
{ "WINDOW_HEIGHT", Game.Renderer.Resolution.Height },
{ "PARENT_WIDTH", parentBounds.Width },
{ "PARENT_HEIGHT", parentBounds.Height }
};
var readOnlySubstitutions = new ReadOnlyDictionary<string, int>(substitutions);
var width = w.Width?.Evaluate(readOnlySubstitutions) ?? 0;
var height = w.Height?.Evaluate(readOnlySubstitutions) ?? 0;
substitutions.Add("WIDTH", width);
substitutions.Add("HEIGHT", height);
if (insideScrollPanel)
w.Bounds = new WidgetBounds(w.Bounds.X, w.Bounds.Y, width, w.Bounds.Height);
else
w.Bounds = new WidgetBounds(
w.X?.Evaluate(readOnlySubstitutions) ?? 0,
w.Y?.Evaluate(readOnlySubstitutions) ?? 0,
width,
height);
foreach (var c in w.Children)
RecalculateWidgetLayout(c, insideScrollPanel || w is ScrollPanelWidget);
}
public static void ShowUIScaleDropdown(DropDownButtonWidget dropdown, GraphicSettings graphicSettings)
{
ScrollItemWidget SetupItem(float o, ScrollItemWidget itemTemplate)
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => graphicSettings.UIScale == o,
() =>
{
Game.RunAfterTick(() =>
{
var oldScale = graphicSettings.UIScale;
graphicSettings.UIScale = o;
Game.Renderer.SetUIScale(o);
RecalculateWidgetLayout(Ui.Root);
Viewport.LastMousePos = (Viewport.LastMousePos.ToFloat2() * oldScale / graphicSettings.UIScale).ToInt2();
});
});
var label = $"{(int)(100 * o)}%";
item.Get<LabelWidget>("LABEL").GetText = () => label;
return item;
}
var viewportSizes = Game.ModData.GetOrCreate<WorldViewportSizes>();
var maxScales = new float2(Game.Renderer.NativeResolution) / new float2(viewportSizes.MinEffectiveResolution);
var maxScale = Math.Min(maxScales.X, maxScales.Y);
var validScales = new[] { 1f, 1.25f, 1.5f, 1.75f, 2f }.Where(x => x <= maxScale);
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, validScales, SetupItem);
}
}
}

View File

@@ -0,0 +1,192 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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 OpenRA.Graphics;
using OpenRA.Mods.Common.Traits;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets.Logic
{
public class GameplaySettingsLogic : ChromeLogic
{
[FluentReference]
const string AutoSaveIntervalOptions = "auto-save-interval.options";
[FluentReference]
const string AutoSaveIntervalDisabled = "auto-save-interval.disabled";
[FluentReference]
const string AutoSaveIntervalMinuteOptions = "auto-save-interval.minute-options";
[FluentReference]
const string AutoSaveMaxFileNumber = "auto-save-max-file-number";
readonly int[] autoSaveSeconds = [0, 10, 30, 45, 60, 120, 180, 300, 600];
readonly int[] autoSaveFileNumbers = [3, 5, 10, 20, 50, 100];
readonly AutoSaveSettings autoSaveSettings;
readonly GameSettings gameSettings;
readonly PlayerSettings playerSettings;
readonly WorldRenderer worldRenderer;
readonly ModData modData;
TextFieldWidget nameTextfield;
[ObjectCreator.UseCtor]
public GameplaySettingsLogic(ModData modData, SettingsLogic settingsLogic, string panelID, string label, WorldRenderer worldRenderer)
{
this.modData = modData;
this.worldRenderer = worldRenderer;
autoSaveSettings = modData.GetSettings<AutoSaveSettings>();
gameSettings = modData.GetSettings<GameSettings>();
playerSettings = modData.GetSettings<PlayerSettings>();
settingsLogic.RegisterSettingsPanel(panelID, label, InitPanel, ResetPanel);
}
Func<bool> InitPanel(Widget panel)
{
var scrollPanel = panel.Get<ScrollPanelWidget>("SETTINGS_SCROLLPANEL");
var world = worldRenderer.World;
var escPressed = false;
nameTextfield = panel.Get<TextFieldWidget>("PLAYERNAME");
nameTextfield.IsDisabled = () => world.Type != WorldType.Shellmap;
nameTextfield.Text = Settings.SanitizedPlayerName(playerSettings.Name);
nameTextfield.OnLoseFocus = () =>
{
if (escPressed)
{
escPressed = false;
return;
}
nameTextfield.Text = nameTextfield.Text.Trim();
if (nameTextfield.Text.Length == 0)
nameTextfield.Text = Settings.SanitizedPlayerName(playerSettings.Name);
else
{
nameTextfield.Text = Settings.SanitizedPlayerName(nameTextfield.Text);
playerSettings.Name = nameTextfield.Text;
}
};
nameTextfield.OnEnterKey = _ => { nameTextfield.YieldKeyboardFocus(); return true; };
nameTextfield.OnEscKey = _ =>
{
nameTextfield.Text = Settings.SanitizedPlayerName(playerSettings.Name);
escPressed = true;
nameTextfield.YieldKeyboardFocus();
return true;
};
var colorManager = modData.DefaultRules.Actors[SystemActors.World].TraitInfo<IColorPickerManagerInfo>();
var colorDropdown = panel.Get<DropDownButtonWidget>("PLAYERCOLOR");
colorDropdown.IsDisabled = () => world.Type != WorldType.Shellmap;
colorDropdown.OnMouseDown = _ => colorManager.ShowColorDropDown(colorDropdown, playerSettings.Color, null, worldRenderer, color =>
{
playerSettings.Color = color;
playerSettings.Save();
});
colorDropdown.Get<ColorBlockWidget>("COLORBLOCK").GetColor = () => playerSettings.Color;
SettingsUtils.BindCheckboxPref(panel, "HIDE_REPLAY_CHAT_CHECKBOX", gameSettings, "HideReplayChat");
var autoSaveIntervalDropDown = panel.Get<DropDownButtonWidget>("AUTO_SAVE_INTERVAL_DROP_DOWN");
autoSaveIntervalDropDown.OnClick = () =>
ShowAutoSaveIntervalDropdown(autoSaveIntervalDropDown, autoSaveSeconds);
autoSaveIntervalDropDown.GetText = () => GetMessageForAutoSaveInterval(autoSaveSettings.AutoSaveInterval);
// Setup dropdown for auto-save number.
var autoSaveNoDropDown = panel.Get<DropDownButtonWidget>("AUTO_SAVE_FILE_NUMBER_DROP_DOWN");
autoSaveNoDropDown.OnMouseDown = _ => ShowAutoSaveFileNumberDropdown(autoSaveNoDropDown, autoSaveFileNumbers);
autoSaveNoDropDown.GetText = () => FluentProvider.GetMessage(AutoSaveMaxFileNumber, "saves", autoSaveSettings.AutoSaveMaxFileCount);
autoSaveNoDropDown.IsDisabled = () => autoSaveSettings.AutoSaveInterval <= 0;
SettingsUtils.AdjustSettingsScrollPanelLayout(scrollPanel);
return () =>
{
nameTextfield.YieldKeyboardFocus();
return false;
};
}
Action ResetPanel(Widget panel)
{
var defaultAutoSaveSettings = new AutoSaveSettings();
var defaultGameSettings = new GameSettings();
var defaultPlayerSettings = new PlayerSettings();
return () =>
{
nameTextfield.Text = playerSettings.Name = defaultPlayerSettings.Name;
playerSettings.Color = defaultPlayerSettings.Color;
autoSaveSettings.AutoSaveInterval = defaultAutoSaveSettings.AutoSaveInterval;
autoSaveSettings.AutoSaveMaxFileCount = defaultAutoSaveSettings.AutoSaveMaxFileCount;
gameSettings.HideReplayChat = defaultGameSettings.HideReplayChat;
};
}
void ShowAutoSaveIntervalDropdown(DropDownButtonWidget dropdown, IEnumerable<int> options)
{
ScrollItemWidget SetupItem(int o, ScrollItemWidget itemTemplate)
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => autoSaveSettings.AutoSaveInterval == o,
() =>
{
autoSaveSettings.AutoSaveInterval = o;
autoSaveSettings.Save();
});
var deviceLabel = item.Get<LabelWidget>("LABEL");
deviceLabel.GetText = () => GetMessageForAutoSaveInterval(o);
return item;
}
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, options, SetupItem);
}
void ShowAutoSaveFileNumberDropdown(DropDownButtonWidget dropdown, IEnumerable<int> options)
{
ScrollItemWidget SetupItem(int o, ScrollItemWidget itemTemplate)
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => autoSaveSettings.AutoSaveMaxFileCount == o,
() =>
{
autoSaveSettings.AutoSaveMaxFileCount = o;
autoSaveSettings.Save();
});
var deviceLabel = item.Get<LabelWidget>("LABEL");
deviceLabel.GetText = () => FluentProvider.GetMessage(AutoSaveMaxFileNumber, "saves", o);
return item;
}
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, options, SetupItem);
}
static string GetMessageForAutoSaveInterval(int value) =>
value switch
{
0 => FluentProvider.GetMessage(AutoSaveIntervalDisabled),
< 60 => FluentProvider.GetMessage(AutoSaveIntervalOptions, "seconds", value),
_ => FluentProvider.GetMessage(AutoSaveIntervalMinuteOptions, "minutes", value / 60)
};
}
}

View File

@@ -0,0 +1,379 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Frozen;
using System.Collections.Generic;
using System.Linq;
using OpenRA.Primitives;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets.Logic
{
[IncludeChromeLogicArgsFluentReferences(nameof(DynamicFluentReferences))]
[IncludeStaticFluentReferences(typeof(KeycodeExts), typeof(ModifiersExts))]
public class HotkeysSettingsLogic : ChromeLogic
{
[FluentReference("key")]
const string OriginalNotice = "label-original-notice";
[FluentReference("key", "context")]
const string DuplicateNotice = "label-duplicate-notice";
[FluentReference]
const string AnyContext = HotkeyDefinition.ContextFluentPrefix + "-any";
public static IEnumerable<(string Key, FluentReferenceAttribute Reference)> DynamicFluentReferences(Dictionary<string, MiniYaml> logicArgs)
{
if (logicArgs.TryGetValue("HotkeyGroups", out var hotkeyGroupsYaml))
foreach (var node in hotkeyGroupsYaml.Nodes)
yield return (node.Key, new FluentReferenceAttribute());
}
readonly ModData modData;
readonly Dictionary<string, MiniYaml> logicArgs;
ScrollPanelWidget hotkeyList;
ButtonWidget selectedHotkeyButton;
HotkeyEntryWidget hotkeyEntryWidget;
HotkeyDefinition duplicateHotkeyDefinition, selectedHotkeyDefinition;
int validHotkeyEntryWidth;
int invalidHotkeyEntryWidth;
bool isHotkeyValid;
bool isHotkeyDefault;
string currentContext = AnyContext;
readonly HashSet<string> contexts = [AnyContext];
readonly Dictionary<string, FrozenSet<string>> hotkeyGroups = [];
TextFieldWidget filterInput;
Widget headerTemplate;
Widget template;
Widget emptyListMessage;
Widget remapDialog;
[ObjectCreator.UseCtor]
public HotkeysSettingsLogic(ModData modData, SettingsLogic settingsLogic, string panelID, string label, Dictionary<string, MiniYaml> logicArgs)
{
this.modData = modData;
this.logicArgs = logicArgs;
settingsLogic.RegisterSettingsPanel(panelID, label, InitPanel, ResetPanel);
}
void BindHotkeyPref(HotkeyDefinition hd, Widget template)
{
var key = template.Clone();
key.Id = hd.Name;
key.IsVisible = () => true;
var desc = FluentProvider.GetMessage(hd.Description) + ":";
key.Get<LabelWidget>("FUNCTION").GetText = () => desc;
var remapButton = key.Get<ButtonWidget>("HOTKEY");
WidgetUtils.TruncateButtonToTooltip(remapButton, modData.Hotkeys[hd.Name].GetValue().DisplayString());
remapButton.IsHighlighted = () => selectedHotkeyDefinition == hd;
var hotkeyValidColor = ChromeMetrics.Get<Color>("HotkeyColor");
var hotkeyInvalidColor = ChromeMetrics.Get<Color>("HotkeyColorInvalid");
remapButton.GetColor = () => hd.HasDuplicates ? hotkeyInvalidColor : hotkeyValidColor;
if (selectedHotkeyDefinition == hd)
{
selectedHotkeyButton = remapButton;
hotkeyEntryWidget.Key = modData.Hotkeys[hd.Name].GetValue();
ValidateHotkey();
}
remapButton.OnClick = () =>
{
selectedHotkeyDefinition = hd;
selectedHotkeyButton = remapButton;
hotkeyEntryWidget.Key = modData.Hotkeys[hd.Name].GetValue();
ValidateHotkey();
if (hd.Readonly)
hotkeyEntryWidget.YieldKeyboardFocus();
else
hotkeyEntryWidget.TakeKeyboardFocus();
};
hotkeyList.AddChild(key);
}
Func<bool> InitPanel(Widget panel)
{
hotkeyList = panel.Get<ScrollPanelWidget>("HOTKEY_LIST");
hotkeyList.Layout = new GridLayout(hotkeyList);
headerTemplate = hotkeyList.Get("HEADER");
template = hotkeyList.Get("TEMPLATE");
emptyListMessage = panel.Get("HOTKEY_EMPTY_LIST");
remapDialog = panel.Get("HOTKEY_REMAP_DIALOG");
foreach (var hd in modData.Hotkeys.Definitions)
contexts.UnionWith(hd.Contexts);
filterInput = panel.Get<TextFieldWidget>("FILTER_INPUT");
filterInput.OnTextEdited = InitHotkeyList;
filterInput.OnEscKey = _ =>
{
if (string.IsNullOrEmpty(filterInput.Text))
filterInput.YieldKeyboardFocus();
else
{
filterInput.Text = "";
filterInput.OnTextEdited();
}
return true;
};
var contextDropdown = panel.GetOrNull<DropDownButtonWidget>("CONTEXT_DROPDOWN");
if (contextDropdown != null)
{
contextDropdown.OnMouseDown = _ => ShowContextDropdown(contextDropdown);
var contextName = new CachedTransform<string, string>(GetContextDisplayName);
contextDropdown.GetText = () => contextName.Update(currentContext);
}
if (logicArgs.TryGetValue("HotkeyGroups", out var hotkeyGroupsYaml))
{
foreach (var hg in hotkeyGroupsYaml.Nodes)
{
var typesNode = hg.Value.NodeWithKeyOrDefault("Types");
if (typesNode != null)
hotkeyGroups.Add(hg.Key, FieldLoader.GetValue<FrozenSet<string>>("Types", typesNode.Value.Value));
}
InitHotkeyRemapDialog(panel);
InitHotkeyList();
}
return () =>
{
hotkeyEntryWidget.Key =
selectedHotkeyDefinition != null ?
modData.Hotkeys[selectedHotkeyDefinition.Name].GetValue() :
Hotkey.Invalid;
hotkeyEntryWidget.ForceYieldKeyboardFocus();
return false;
};
}
Action ResetPanel(Widget panel)
{
return () =>
{
foreach (var hd in modData.Hotkeys.Definitions)
{
modData.Hotkeys.Set(hd.Name, hd.Default);
var hotkeyButton = panel.GetOrNull(hd.Name)?.Get<ButtonWidget>("HOTKEY");
if (hotkeyButton != null)
WidgetUtils.TruncateButtonToTooltip(hotkeyButton, hd.Default.DisplayString());
}
};
}
void InitHotkeyList()
{
hotkeyList.RemoveChildren();
selectedHotkeyDefinition = null;
foreach (var hg in hotkeyGroups)
{
var typesInGroup = hg.Value;
var keysInGroup = modData.Hotkeys.Definitions
.Where(hd => IsHotkeyVisibleInFilter(hd) && hd.Types.Overlaps(typesInGroup))
.ToList();
if (keysInGroup.Count == 0)
continue;
var header = headerTemplate.Clone();
var groupName = FluentProvider.GetMessage(hg.Key);
header.Get<LabelWidget>("LABEL").GetText = () => groupName;
hotkeyList.AddChild(header);
var added = new HashSet<HotkeyDefinition>();
foreach (var type in typesInGroup)
{
foreach (var hd in keysInGroup.Where(k => k.Types.Contains(type)))
{
if (added.Add(hd))
{
selectedHotkeyDefinition ??= hd;
BindHotkeyPref(hd, template);
}
}
}
}
emptyListMessage.Visible = selectedHotkeyDefinition == null;
remapDialog.Visible = selectedHotkeyDefinition != null;
hotkeyList.ScrollToTop();
}
void InitHotkeyRemapDialog(Widget panel)
{
var label = panel.Get<LabelWidget>("HOTKEY_LABEL");
var labelText = new CachedTransform<HotkeyDefinition, string>(
hd => (hd != null ? FluentProvider.GetMessage(hd.Description) : "") + ":");
label.IsVisible = () => selectedHotkeyDefinition != null;
label.GetText = () => labelText.Update(selectedHotkeyDefinition);
var duplicateNotice = panel.Get<LabelWidget>("DUPLICATE_NOTICE");
duplicateNotice.TextColor = ChromeMetrics.Get<Color>("NoticeErrorColor");
duplicateNotice.IsVisible = () => !isHotkeyValid;
var duplicateNoticeText = new CachedTransform<HotkeyDefinition, string>(hd =>
hd != null
? FluentProvider.GetMessage(
DuplicateNotice,
"key", FluentProvider.GetMessage(hd.Description),
"context", FluentProvider.GetMessage(hd.Contexts.First(c => selectedHotkeyDefinition.Contexts.Contains(c))))
: "");
duplicateNotice.GetText = () => duplicateNoticeText.Update(duplicateHotkeyDefinition);
var originalNotice = panel.Get<LabelWidget>("ORIGINAL_NOTICE");
originalNotice.TextColor = ChromeMetrics.Get<Color>("NoticeInfoColor");
originalNotice.IsVisible = () => isHotkeyValid && !isHotkeyDefault;
var originalNoticeText = new CachedTransform<HotkeyDefinition, string>(hd =>
FluentProvider.GetMessage(OriginalNotice, "key", hd?.Default.DisplayString()));
originalNotice.GetText = () => originalNoticeText.Update(selectedHotkeyDefinition);
var readonlyNotice = panel.Get<LabelWidget>("READONLY_NOTICE");
readonlyNotice.TextColor = ChromeMetrics.Get<Color>("NoticeInfoColor");
readonlyNotice.IsVisible = () => selectedHotkeyDefinition.Readonly;
var resetButton = panel.Get<ButtonWidget>("RESET_HOTKEY_BUTTON");
resetButton.IsDisabled = () => isHotkeyDefault || selectedHotkeyDefinition.Readonly;
resetButton.OnClick = ResetHotkey;
var clearButton = panel.Get<ButtonWidget>("CLEAR_HOTKEY_BUTTON");
clearButton.IsDisabled = () => selectedHotkeyDefinition.Readonly || !hotkeyEntryWidget.Key.IsValid();
clearButton.OnClick = ClearHotkey;
var overrideButton = panel.Get<ButtonWidget>("OVERRIDE_HOTKEY_BUTTON");
overrideButton.IsDisabled = () => isHotkeyValid;
overrideButton.IsVisible = () => !isHotkeyValid && !duplicateHotkeyDefinition.Readonly;
overrideButton.OnClick = OverrideHotkey;
hotkeyEntryWidget = panel.Get<HotkeyEntryWidget>("HOTKEY_ENTRY");
hotkeyEntryWidget.IsValid = () => isHotkeyValid;
hotkeyEntryWidget.OnLoseFocus = ValidateHotkey;
hotkeyEntryWidget.OnEscKey = _ =>
hotkeyEntryWidget.Key = modData.Hotkeys[selectedHotkeyDefinition.Name].GetValue();
hotkeyEntryWidget.IsDisabled = () => selectedHotkeyDefinition.Readonly;
validHotkeyEntryWidth = hotkeyEntryWidget.Bounds.Width;
invalidHotkeyEntryWidth = validHotkeyEntryWidth - (clearButton.Bounds.X - overrideButton.Bounds.X);
}
void ValidateHotkey()
{
if (selectedHotkeyDefinition == null)
return;
duplicateHotkeyDefinition = modData.Hotkeys.GetFirstDuplicate(selectedHotkeyDefinition, hotkeyEntryWidget.Key);
isHotkeyValid = duplicateHotkeyDefinition == null || selectedHotkeyDefinition.Readonly;
isHotkeyDefault =
hotkeyEntryWidget.Key == selectedHotkeyDefinition.Default ||
(!hotkeyEntryWidget.Key.IsValid() && !selectedHotkeyDefinition.Default.IsValid());
if (isHotkeyValid)
{
hotkeyEntryWidget.Bounds.Width = validHotkeyEntryWidth;
SaveHotkey();
}
else
{
hotkeyEntryWidget.Bounds.Width = duplicateHotkeyDefinition.Readonly ? validHotkeyEntryWidth : invalidHotkeyEntryWidth;
hotkeyEntryWidget.TakeKeyboardFocus();
}
}
void SaveHotkey()
{
if (selectedHotkeyDefinition.Readonly)
return;
WidgetUtils.TruncateButtonToTooltip(selectedHotkeyButton, hotkeyEntryWidget.Key.DisplayString());
modData.Hotkeys.Set(selectedHotkeyDefinition.Name, hotkeyEntryWidget.Key);
modData.Hotkeys.Save();
}
void ResetHotkey()
{
hotkeyEntryWidget.Key = selectedHotkeyDefinition.Default;
hotkeyEntryWidget.YieldKeyboardFocus();
}
void ClearHotkey()
{
hotkeyEntryWidget.Key = Hotkey.Invalid;
hotkeyEntryWidget.YieldKeyboardFocus();
}
void OverrideHotkey()
{
var duplicateHotkeyButton = hotkeyList.GetOrNull<ContainerWidget>(duplicateHotkeyDefinition.Name)?.Get<ButtonWidget>("HOTKEY");
if (duplicateHotkeyButton != null)
WidgetUtils.TruncateButtonToTooltip(duplicateHotkeyButton, Hotkey.Invalid.DisplayString());
modData.Hotkeys.Set(duplicateHotkeyDefinition.Name, Hotkey.Invalid);
modData.Hotkeys.Save();
hotkeyEntryWidget.YieldKeyboardFocus();
}
bool IsHotkeyVisibleInFilter(HotkeyDefinition hd)
{
var filter = filterInput.Text;
var isFilteredByName = string.IsNullOrWhiteSpace(filter) ||
FluentProvider.GetMessage(hd.Description).Contains(filter, StringComparison.CurrentCultureIgnoreCase);
var isFilteredByContext = currentContext == AnyContext || hd.Contexts.Contains(currentContext);
return isFilteredByName && isFilteredByContext;
}
bool ShowContextDropdown(DropDownButtonWidget dropdown)
{
hotkeyEntryWidget.YieldKeyboardFocus();
var contextName = new CachedTransform<string, string>(GetContextDisplayName);
ScrollItemWidget SetupItem(string context, ScrollItemWidget itemTemplate)
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => currentContext == context,
() => { currentContext = context; InitHotkeyList(); });
item.Get<LabelWidget>("LABEL").GetText = () => contextName.Update(context);
return item;
}
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 280, contexts, SetupItem);
return true;
}
static string GetContextDisplayName(string context)
{
if (string.IsNullOrEmpty(context))
context = AnyContext;
return FluentProvider.GetMessage(context);
}
}
}

View File

@@ -0,0 +1,232 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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 OpenRA.Primitives;
using OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets.Logic
{
public class InputSettingsLogic : ChromeLogic
{
[FluentReference]
const string Classic = "options-control-scheme.classic";
[FluentReference]
const string Modern = "options-control-scheme.modern";
[FluentReference]
const string OtherRTS = "options-control-scheme.otherrts";
[FluentReference]
const string Disabled = "options-mouse-scroll-type.disabled";
[FluentReference]
const string Standard = "options-mouse-scroll-type.standard";
[FluentReference]
const string Inverted = "options-mouse-scroll-type.inverted";
[FluentReference]
const string Joystick = "options-mouse-scroll-type.joystick";
readonly GameSettings gameSettings;
readonly Dictionary<MouseControlStyle, string> controlTypes;
[ObjectCreator.UseCtor]
public InputSettingsLogic(ModData modData, SettingsLogic settingsLogic, string panelID, string label)
{
controlTypes = new Dictionary<MouseControlStyle, string>
{
{ MouseControlStyle.Classic, FluentProvider.GetMessage(Classic) },
{ MouseControlStyle.Modern, FluentProvider.GetMessage(Modern) },
{ MouseControlStyle.OtherRTS, FluentProvider.GetMessage(OtherRTS) },
};
gameSettings = modData.GetSettings<GameSettings>();
settingsLogic.RegisterSettingsPanel(panelID, label, InitPanel, ResetPanel);
}
Func<bool> InitPanel(Widget panel)
{
var scrollPanel = panel.Get<ScrollPanelWidget>("SETTINGS_SCROLLPANEL");
SettingsUtils.BindCheckboxPref(panel, "ALTERNATE_SCROLL_CHECKBOX", gameSettings, "UseAlternateScrollButton");
SettingsUtils.BindCheckboxPref(panel, "EDGESCROLL_CHECKBOX", gameSettings, "ViewportEdgeScroll");
SettingsUtils.BindCheckboxPref(panel, "LOCKMOUSE_CHECKBOX", gameSettings, "LockMouseWindow");
SettingsUtils.BindSliderPref(panel, "ZOOMSPEED_SLIDER", gameSettings, "ZoomSpeed");
SettingsUtils.BindSliderPref(panel, "SCROLLSPEED_SLIDER", gameSettings, "ViewportEdgeScrollStep");
SettingsUtils.BindSliderPref(panel, "UI_SCROLLSPEED_SLIDER", gameSettings, "UIScrollSpeed");
var mouseControlDropdown = panel.Get<DropDownButtonWidget>("MOUSE_CONTROL_DROPDOWN");
mouseControlDropdown.OnMouseDown = _ => ShowMouseControlDropdown(mouseControlDropdown, controlTypes, gameSettings);
mouseControlDropdown.GetText = () => controlTypes[gameSettings.MouseControlStyle];
var mouseScrollDropdown = panel.Get<DropDownButtonWidget>("MOUSE_SCROLL_TYPE_DROPDOWN");
mouseScrollDropdown.OnMouseDown = _ => ShowMouseScrollDropdown(mouseScrollDropdown, gameSettings);
// MouseScroll can change, must display latest value.
#pragma warning disable IDE0200 // Remove unnecessary lambda expression
mouseScrollDropdown.GetText = () => gameSettings.MouseScroll.ToString();
#pragma warning restore IDE0200
var mouseControlDescClassic = panel.Get("MOUSE_CONTROL_DESC_CLASSIC");
mouseControlDescClassic.IsVisible = () => gameSettings.MouseControlStyle == MouseControlStyle.Classic;
var mouseControlDescModern = panel.Get("MOUSE_CONTROL_DESC_MODERN");
mouseControlDescModern.IsVisible = () => gameSettings.MouseControlStyle == MouseControlStyle.Modern;
var mouseControlDescOtherRTS = panel.Get("MOUSE_CONTROL_DESC_OTHERRTS");
mouseControlDescOtherRTS.IsVisible = () => gameSettings.MouseControlStyle == MouseControlStyle.OtherRTS;
foreach (var container in new[] { mouseControlDescClassic, mouseControlDescModern, mouseControlDescOtherRTS })
{
var classicScrollRight = container.Get("DESC_SCROLL_RIGHT");
classicScrollRight.IsVisible = () => (gameSettings.MouseControlStyle == MouseControlStyle.Classic) ^ gameSettings.UseAlternateScrollButton;
var classicScrollMiddle = container.Get("DESC_SCROLL_MIDDLE");
classicScrollMiddle.IsVisible = () => (gameSettings.MouseControlStyle != MouseControlStyle.Classic) ^ gameSettings.UseAlternateScrollButton;
var zoomDesc = container.Get("DESC_ZOOM");
zoomDesc.IsVisible = () => gameSettings.ZoomModifier == Modifiers.None;
var zoomDescModifier = container.Get<LabelWidget>("DESC_ZOOM_MODIFIER");
zoomDescModifier.IsVisible = () => gameSettings.ZoomModifier != Modifiers.None;
var zoomDescModifierTemplate = zoomDescModifier.GetText();
var zoomDescModifierLabel = new CachedTransform<Modifiers, string>(
mod => zoomDescModifierTemplate.Replace("MODIFIER", mod.ToString()));
zoomDescModifier.GetText = () => zoomDescModifierLabel.Update(gameSettings.ZoomModifier);
var edgescrollDesc = container.Get<LabelWidget>("DESC_EDGESCROLL");
edgescrollDesc.IsVisible = () => gameSettings.ViewportEdgeScroll;
}
// Apply mouse focus preferences immediately
var lockMouseCheckbox = panel.Get<CheckboxWidget>("LOCKMOUSE_CHECKBOX");
var oldOnClick = lockMouseCheckbox.OnClick;
lockMouseCheckbox.OnClick = () =>
{
// Still perform the old behaviour for clicking the checkbox, before
// applying the changes live.
oldOnClick();
MakeMouseFocusSettingsLive(gameSettings);
};
var zoomModifierDropdown = panel.Get<DropDownButtonWidget>("ZOOM_MODIFIER");
zoomModifierDropdown.OnMouseDown = _ => ShowZoomModifierDropdown(zoomModifierDropdown, gameSettings);
// ZoomModifier can change, must display latest value.
#pragma warning disable IDE0200 // Remove unnecessary lambda expression
zoomModifierDropdown.GetText = () => gameSettings.ZoomModifier.ToString();
#pragma warning restore IDE0200
SettingsUtils.AdjustSettingsScrollPanelLayout(scrollPanel);
return () => false;
}
Action ResetPanel(Widget panel)
{
var defaultGameSettings = new GameSettings();
return () =>
{
gameSettings.MouseControlStyle = defaultGameSettings.MouseControlStyle;
gameSettings.MouseScroll = defaultGameSettings.MouseScroll;
gameSettings.UseAlternateScrollButton = defaultGameSettings.UseAlternateScrollButton;
gameSettings.LockMouseWindow = defaultGameSettings.LockMouseWindow;
gameSettings.ViewportEdgeScroll = defaultGameSettings.ViewportEdgeScroll;
gameSettings.ViewportEdgeScrollStep = defaultGameSettings.ViewportEdgeScrollStep;
gameSettings.ZoomSpeed = defaultGameSettings.ZoomSpeed;
gameSettings.UIScrollSpeed = defaultGameSettings.UIScrollSpeed;
gameSettings.ZoomModifier = defaultGameSettings.ZoomModifier;
panel.Get<SliderWidget>("SCROLLSPEED_SLIDER").Value = gameSettings.ViewportEdgeScrollStep;
panel.Get<SliderWidget>("UI_SCROLLSPEED_SLIDER").Value = gameSettings.UIScrollSpeed;
MakeMouseFocusSettingsLive(gameSettings);
};
}
public static void ShowMouseControlDropdown(DropDownButtonWidget dropdown, Dictionary<MouseControlStyle, string> controlTypes, GameSettings gameSettings)
{
ScrollItemWidget SetupItem(MouseControlStyle o, ScrollItemWidget itemTemplate)
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => gameSettings.MouseControlStyle == o,
() => gameSettings.MouseControlStyle = o);
var label = controlTypes[o];
item.Get<LabelWidget>("LABEL").GetText = () => label;
return item;
}
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, controlTypes.Keys, SetupItem);
}
static void ShowMouseScrollDropdown(DropDownButtonWidget dropdown, GameSettings gameSettings)
{
var options = new Dictionary<string, MouseScrollType>()
{
{ FluentProvider.GetMessage(Disabled), MouseScrollType.Disabled },
{ FluentProvider.GetMessage(Standard), MouseScrollType.Standard },
{ FluentProvider.GetMessage(Inverted), MouseScrollType.Inverted },
{ FluentProvider.GetMessage(Joystick), MouseScrollType.Joystick },
};
ScrollItemWidget SetupItem(string o, ScrollItemWidget itemTemplate)
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => gameSettings.MouseScroll == options[o],
() => gameSettings.MouseScroll = options[o]);
item.Get<LabelWidget>("LABEL").GetText = () => o;
return item;
}
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, options.Keys, SetupItem);
}
static void ShowZoomModifierDropdown(DropDownButtonWidget dropdown, GameSettings gameSettings)
{
var options = new Dictionary<string, Modifiers>()
{
{ ModifiersExts.DisplayString(Modifiers.Alt), Modifiers.Alt },
{ ModifiersExts.DisplayString(Modifiers.Ctrl), Modifiers.Ctrl },
{ ModifiersExts.DisplayString(Modifiers.Meta), Modifiers.Meta },
{ ModifiersExts.DisplayString(Modifiers.Shift), Modifiers.Shift },
{ ModifiersExts.DisplayString(Modifiers.None), Modifiers.None }
};
ScrollItemWidget SetupItem(string o, ScrollItemWidget itemTemplate)
{
var item = ScrollItemWidget.Setup(itemTemplate,
() => gameSettings.ZoomModifier == options[o],
() => gameSettings.ZoomModifier = options[o]);
item.Get<LabelWidget>("LABEL").GetText = () => o;
return item;
}
dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, options.Keys, SetupItem);
}
static void MakeMouseFocusSettingsLive(GameSettings gameSettings)
{
if (gameSettings.LockMouseWindow)
Game.Renderer.GrabWindowMouseFocus();
else
Game.Renderer.ReleaseWindowMouseFocus();
}
}
}

View File

@@ -0,0 +1,197 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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.Widgets;
namespace OpenRA.Mods.Common.Widgets.Logic
{
public interface ISettingsLogic
{
void RegisterSettingsPanel(string panelID, string label, Func<Widget, Func<bool>> init, Func<Widget, Action> reset);
}
[IncludeChromeLogicArgsFluentReferences(nameof(DynamicFluentReferences))]
public class SettingsLogic : ChromeLogic, ISettingsLogic
{
[FluentReference]
const string SettingsSaveTitle = "dialog-settings-save.title";
[FluentReference]
const string SettingsSavePrompt = "dialog-settings-save.prompt";
[FluentReference]
const string SettingsSaveCancel = "dialog-settings-save.cancel";
[FluentReference]
const string RestartTitle = "dialog-settings-restart.title";
[FluentReference]
const string RestartPrompt = "dialog-settings-restart.prompt";
[FluentReference]
const string RestartAccept = "dialog-settings-restart.confirm";
[FluentReference]
const string RestartCancel = "dialog-settings-restart.cancel";
[FluentReference("panel")]
const string ResetTitle = "dialog-settings-reset.title";
[FluentReference]
const string ResetPrompt = "dialog-settings-reset.prompt";
[FluentReference]
const string ResetAccept = "dialog-settings-reset.confirm";
[FluentReference]
const string ResetCancel = "dialog-settings-reset.cancel";
public static IEnumerable<(string Key, FluentReferenceAttribute Reference)> DynamicFluentReferences(Dictionary<string, MiniYaml> logicArgs)
{
if (logicArgs.TryGetValue("Panels", out var settingsPanels))
foreach (var node in settingsPanels.Nodes)
yield return (node.Value.Value, new FluentReferenceAttribute());
}
readonly Dictionary<string, Func<bool>> leavePanelActions = [];
readonly Dictionary<string, Action> resetPanelActions = [];
readonly Widget panelContainer, tabContainer;
readonly ButtonWidget tabTemplate;
readonly int2 buttonStride;
readonly List<ButtonWidget> buttons = [];
readonly Dictionary<string, string> panels = [];
string activePanel;
bool needsRestart = false;
[ObjectCreator.UseCtor]
public SettingsLogic(Widget widget, Action onExit, WorldRenderer worldRenderer, Dictionary<string, MiniYaml> logicArgs, ModData modData)
{
panelContainer = widget.Get("PANEL_CONTAINER");
var panelTemplate = panelContainer.Get<ContainerWidget>("PANEL_TEMPLATE");
panelContainer.RemoveChild(panelTemplate);
tabContainer = widget.Get("SETTINGS_TAB_CONTAINER");
tabTemplate = tabContainer.Get<ButtonWidget>("BUTTON_TEMPLATE");
tabContainer.RemoveChild(tabTemplate);
if (logicArgs.TryGetValue("ButtonStride", out var buttonStrideNode))
buttonStride = FieldLoader.GetValue<int2>("ButtonStride", buttonStrideNode.Value);
if (logicArgs.TryGetValue("Panels", out var settingsPanels))
{
panels = settingsPanels.ToDictionary(kv => kv.Value);
foreach (var panel in panels)
{
var container = panelTemplate.Clone();
container.Id = panel.Key;
panelContainer.AddChild(container);
Game.LoadWidget(worldRenderer.World, panel.Key, container, new WidgetArgs()
{
{ "settingsLogic", this },
{ "panelID", panel.Key },
{ "label", FluentProvider.GetMessage(panel.Value) }
});
}
}
widget.Get<ButtonWidget>("BACK_BUTTON").OnClick = () =>
{
needsRestart |= leavePanelActions[activePanel]();
Game.Settings.Save();
void CloseAndExit() { Ui.CloseWindow(); onExit(); }
if (needsRestart)
{
void NoRestart() => ConfirmationDialogs.ButtonPrompt(modData,
title: SettingsSaveTitle,
text: SettingsSavePrompt,
onCancel: CloseAndExit,
cancelText: SettingsSaveCancel);
if (!Game.ExternalMods.TryGetValue(ExternalMod.MakeKey(Game.ModData.Manifest), out var external))
{
NoRestart();
return;
}
ConfirmationDialogs.ButtonPrompt(modData,
title: RestartTitle,
text: RestartPrompt,
onConfirm: () => Game.SwitchToExternalMod(external, null, NoRestart),
confirmText: RestartAccept,
onCancel: CloseAndExit,
cancelText: RestartCancel);
}
else
CloseAndExit();
};
widget.Get<ButtonWidget>("RESET_BUTTON").OnClick = () =>
{
void Reset()
{
resetPanelActions[activePanel]();
Game.Settings.Save();
}
ConfirmationDialogs.ButtonPrompt(modData,
title: ResetTitle,
text: ResetPrompt,
titleArguments: ["panel", panels[activePanel]],
onConfirm: Reset,
confirmText: ResetAccept,
onCancel: () => { },
cancelText: ResetCancel);
};
}
public void RegisterSettingsPanel(string panelID, string label, Func<Widget, Func<bool>> init, Func<Widget, Action> reset)
{
var panel = panelContainer.Get(panelID);
activePanel ??= panelID;
panel.IsVisible = () => activePanel == panelID;
leavePanelActions.Add(panelID, init(panel));
resetPanelActions.Add(panelID, reset(panel));
var tab = tabTemplate.Clone();
var lastButton = buttons.LastOrDefault();
if (lastButton != null)
{
tab.Bounds.X = lastButton.Bounds.X + buttonStride.X;
tab.Bounds.Y = lastButton.Bounds.Y + buttonStride.Y;
}
tab.Id = panelID;
tab.GetText = () => label;
tab.IsHighlighted = () => activePanel == panelID;
tab.OnClick = () =>
{
needsRestart |= leavePanelActions[activePanel]();
Game.Settings.Save();
activePanel = panelID;
};
tabContainer.AddChild(tab);
buttons.Add(tab);
}
}
}

View File

@@ -0,0 +1,77 @@
#region Copyright & License Information
/*
* Copyright (c) The OpenRA Developers and Contributors
* 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 OpenRA.Widgets;
namespace OpenRA.Mods.Common.Widgets.Logic
{
public static class SettingsUtils
{
public static void BindCheckboxPref(Widget parent, string id, object group, string pref)
{
var field = group.GetType().GetField(pref);
if (field == null)
throw new InvalidOperationException($"{group.GetType().Name} does not contain a preference type {pref}");
var cb = parent.Get<CheckboxWidget>(id);
cb.IsChecked = () => (bool)field.GetValue(group);
cb.OnClick = () => field.SetValue(group, cb.IsChecked() ^ true);
}
public static void BindSliderPref(Widget parent, string id, object group, string pref)
{
var field = group.GetType().GetField(pref);
if (field == null)
throw new InvalidOperationException($"{group.GetType().Name} does not contain a preference type {pref}");
var ss = parent.Get<SliderWidget>(id);
ss.Value = (float)field.GetValue(group);
ss.OnChange += x => field.SetValue(group, x);
}
public static void BindIntSliderPref(Widget parent, string id, object group, string pref)
{
var field = group.GetType().GetField(pref);
if (field == null)
throw new InvalidOperationException($"{group.GetType().Name} does not contain a preference type {pref}");
var ss = parent.Get<SliderWidget>(id);
ss.Value = (int)field.GetValue(group);
ss.OnChange += x => field.SetValue(group, (int)x);
}
public static void AdjustSettingsScrollPanelLayout(ScrollPanelWidget scrollPanel)
{
foreach (var row in scrollPanel.Children)
{
if (row.Children.Count == 0)
continue;
var hasVisibleChildren = false;
foreach (var container in row.Children)
{
if (container.IsVisible())
{
hasVisibleChildren = true;
break;
}
}
if (!hasVisibleChildren)
row.Visible = false;
}
scrollPanel.Layout.AdjustChildren();
}
}
}