Add Segmented Control UI for Output Mode Option

This commit is contained in:
copecone committed 2026-10-22 11:00:05 +09:00
1 parent 5efe1d0dd2
commit 4179ed6696
6 files changed
+684 -72

No files matched your search

+263 -72
View File
@@ -13,6 +13,7 @@ using Newtonsoft.Json;
using O5Kit.Control;
using O5Kit.Core;
using O5Kit.Factory;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
@@ -105,25 +106,34 @@ internal static class RenderConfigWindow
private static O5Slider? _renderHeightSlider;
private static O5Slider? _delayAfterEndSlider;
private static O5Toggle? _showJudgementToggle;
private static SegmentedControl<RenderConfig.RenderOutputMode>? _outputModeControl;
private static O5Dropdown<Codec>? _codecDropdown;
private static TextMeshProUGUI? _codecLabel;
private static ITweenHandle? _codecLabelTween;
private static O5Dropdown<RateControlMode>? _rateControlDropdown;
private static TextMeshProUGUI? _rateControlLabel;
private static ITweenHandle? _rateControlLabelTween;
// Bitrate / max bitrate / QP inputs keep their label and the running label color tween, so UpdateRateControlUI
// can dim the label together with the blocked input (see SetFieldBlocked).
private static O5InputField? _bitrateInput;
private static TMPro.TextMeshProUGUI? _bitrateLabel;
private static TextMeshProUGUI? _bitrateLabel;
private static ITweenHandle? _bitrateLabelTween;
private static O5InputField? _maxBitrateInput;
private static TMPro.TextMeshProUGUI? _maxBitrateLabel;
private static TextMeshProUGUI? _maxBitrateLabel;
private static ITweenHandle? _maxBitrateLabelTween;
private static O5InputField? _qpInput;
private static TMPro.TextMeshProUGUI? _qpLabel;
private static TextMeshProUGUI? _qpLabel;
private static ITweenHandle? _qpLabelTween;
private static O5Slider? _keyframeIntervalSlider;
private static O5Toggle? _forceSoftwareToggle;
private static TextMeshProUGUI? _forceSoftwareLabel;
private static ITweenHandle? _forceSoftwareLabelTween;
private static O5Slider? _keyframeIntervalSlider;
private static O5Toggle? _renderAudioToggle;
private static O5Dropdown<int>? _sampleRateDropdown;
@@ -376,7 +386,8 @@ internal static class RenderConfigWindow
var redTheme = ctx.Theme with
{
ObjectButton = ctx.Theme.SoftRed,
ButtonHover = ctx.Theme.SoftRed * 0.8f
ButtonHover = ctx.Theme.SoftRed * 0.8f,
ButtonPressed = ctx.Theme.SoftRed * 0.8f
};
var redCtx = new O5Context(
config: ctx.Config,
@@ -421,7 +432,7 @@ internal static class RenderConfigWindow
_renderFpsSlider = CreateSlider(
ctx: ctx, parent: CreateFieldSlot(topLeft),
labelKey: "ui.render_config.render_fps", id: "slider_render_fps",
defaultValue: 60, min: 0.01, max: 3000, value: 60,
defaultValue: 60, min: 0.001, max: 3000, value: 60,
format: "F3", clampMode: ClampMode.All,
sliderFilter: value => Snap(renderFpsSnap, value),
onChanged: val => { config.RenderFPS = val; MarkConfigModified(); }
@@ -431,7 +442,7 @@ internal static class RenderConfigWindow
_videoFPSSlider = CreateSlider(
ctx: ctx, parent: CreateFieldSlot(topRight),
labelKey: "ui.render_config.video_fps", id: "slider_video_fps",
defaultValue: 60, min: 0.01, max: 600, value: 60,
defaultValue: 60, min: 0.001, max: 600, value: 60,
format: "F3", clampMode: ClampMode.All,
sliderFilter: value => Snap(videoFpsSnap, value),
onChanged: val => { config.VideoFPS = val; MarkConfigModified(); }
@@ -478,6 +489,28 @@ internal static class RenderConfigWindow
onChanged: val => { config.ShowJudgementText = val; MarkConfigModified(); },
generateLabel: false
);
// Output Mode (RenderConfig.OutputMode): Video vs PNG Sequence
_outputModeControl = CreateSegmentedField(
ctx: ctx,
parent: scrollContent,
labelKey: "ui.render_config.output_mode",
defaultValue: RenderConfig.RenderOutputMode.Video,
value: config.OutputMode,
options: new SegmentOption<RenderConfig.RenderOutputMode>[]
{
new(RenderConfig.RenderOutputMode.Video, "ui.render_config.output_mode_video"),
new(RenderConfig.RenderOutputMode.PNGSequence, "ui.render_config.output_mode_png_sequence")
},
id: "seg_output_mode",
onChanged: val =>
{
config.OutputMode = val;
UpdateUIBlock(config);
MarkConfigModified();
}
);
// ---------------------------------------------------------------------------------------------
// Section 3: Video Encoding card (RenderConfig.VideoConfig)
@@ -502,11 +535,11 @@ internal static class RenderConfigWindow
"ui.render_config.codec",
Codec.H264, config.VideoConfig.Codec,
codecs, c => c.ToString(),
"dd_video_codec",
"dd_video_codec", out _codecLabel,
c =>
{
config.VideoConfig.Codec = c;
UpdateVideoCodecUI(c, config);
UpdateSampleRateUI(c, config);
MarkConfigModified();
});
@@ -517,7 +550,7 @@ internal static class RenderConfigWindow
"ui.render_config.bitrate",
"30000", config.VideoConfig.Bitrate.ToString(),
"input_bitrate", out _bitrateLabel,
TMPro.TMP_InputField.CharacterValidation.Integer,
TMP_InputField.CharacterValidation.Integer,
val => { if (int.TryParse(val, out var b) && b > 0) config.VideoConfig.Bitrate = b; MarkConfigModified(); }
);
@@ -526,7 +559,7 @@ internal static class RenderConfigWindow
"ui.render_config.qp",
"18", config.VideoConfig.QP.ToString(),
"input_qp", out _qpLabel,
TMPro.TMP_InputField.CharacterValidation.Integer,
TMP_InputField.CharacterValidation.Integer,
val => { if (int.TryParse(val, out var qp) && qp >= 0) config.VideoConfig.QP = qp; MarkConfigModified(); }
);
@@ -536,11 +569,11 @@ internal static class RenderConfigWindow
"ui.render_config.rate_control",
RateControlMode.CBR, config.VideoConfig.RateControlMode,
rateModes, m => m.ToString(),
"dd_rate_control",
"dd_rate_control", out _rateControlLabel,
m =>
{
config.VideoConfig.RateControlMode = m;
UpdateRateControlUI(m);
UpdateUIBlock(config);
MarkConfigModified();
}
);
@@ -550,7 +583,7 @@ internal static class RenderConfigWindow
"ui.render_config.max_bitrate",
"50000", config.VideoConfig.MaxBitrate.ToString(),
"input_max_bitrate", out _maxBitrateLabel,
TMPro.TMP_InputField.CharacterValidation.Integer,
TMP_InputField.CharacterValidation.Integer,
val => { if (int.TryParse(val, out var mb) && mb > 0) config.VideoConfig.MaxBitrate = mb; MarkConfigModified(); }
);
@@ -560,7 +593,7 @@ internal static class RenderConfigWindow
defaultValue: false,
value: config.VideoConfig.ForceSoftwareEncoder,
toggleTextKey: "ui.render_config.force_software",
id: "toggle_force_software",
id: "toggle_force_software", out _forceSoftwareLabel,
onChanged: val => { config.VideoConfig.ForceSoftwareEncoder = val; MarkConfigModified(); },
generateLabel: true
);
@@ -826,7 +859,7 @@ internal static class RenderConfigWindow
"ui.render_config.random_seed",
"", config.AdvancedConfig.RandomSeed?.ToString() ?? "",
"input_random_seed",
TMPro.TMP_InputField.CharacterValidation.Integer,
TMP_InputField.CharacterValidation.Integer,
val =>
{
if (int.TryParse(val, out var s)) config.AdvancedConfig.RandomSeed = s;
@@ -1021,7 +1054,7 @@ internal static class RenderConfigWindow
// RenderMod.ExpirationTime (GetVersionText)
var versionLabel = O5Factory.ControlText(ctx, scrollContent, 12f, noPad: true);
LocalizedText.Bind(versionLabel, GetVersionText);
versionLabel.alignment = TMPro.TextAlignmentOptions.Center;
versionLabel.alignment = TextAlignmentOptions.Center;
versionLabel.color = ctx.Theme.TextDim;
// ReSharper disable once InconsistentNaming
@@ -1071,6 +1104,7 @@ internal static class RenderConfigWindow
_renderHeightSlider?.Set(config.RenderHeight, false, noFilter: true);
_delayAfterEndSlider?.Set(config.DelayAfterEnd, false, noFilter: true);
_showJudgementToggle?.Set(config.ShowJudgementText, false);
_outputModeControl?.Set(config.OutputMode, false);
_codecDropdown?.Set(config.VideoConfig.Codec, false);
_rateControlDropdown?.Set(config.VideoConfig.RateControlMode, false);
@@ -1118,8 +1152,8 @@ internal static class RenderConfigWindow
// The sample rate list depends on the codec. UpdateVideoCodecUI writes the dropdown's resulting value back into
// the config: O5Dropdown.SetValues selects the first listed rate when the current one is not in the new list,
// so a rate the codec does not support becomes the codec's first rate.
UpdateVideoCodecUI(config.VideoConfig.Codec, config);
UpdateRateControlUI(config.VideoConfig.RateControlMode, noAnimate: true);
UpdateSampleRateUI(config.VideoConfig.Codec, config);
UpdateUIBlock(config, noAnimate: true);
UpdatePresetLabel(config);
}
@@ -1252,6 +1286,7 @@ internal static class RenderConfigWindow
if (_renderHeightSlider != null) target.RenderHeight = (int)Math.Round(_renderHeightSlider.Value);
if (_delayAfterEndSlider != null) target.DelayAfterEnd = _delayAfterEndSlider.Value;
if (_showJudgementToggle != null) target.ShowJudgementText = _showJudgementToggle.Value;
if (_outputModeControl != null) target.OutputMode = _outputModeControl.Value;
if (_codecDropdown != null) target.VideoConfig.Codec = _codecDropdown.Value;
if (_rateControlDropdown != null) target.VideoConfig.RateControlMode = _rateControlDropdown.Value;
@@ -1721,10 +1756,8 @@ internal static class RenderConfigWindow
hLayout.spacing = spacing;
hLayout.padding = new RectOffset(0, 0, 0, 0);
hLayout.childAlignment = TextAnchor.UpperCenter;
hLayout.childControlWidth = true;
hLayout.childControlHeight = true;
hLayout.childForceExpandWidth = true;
hLayout.childForceExpandHeight = false;
hLayout.childControlWidth = hLayout.childControlHeight = true;
hLayout.childForceExpandWidth = true; hLayout.childForceExpandHeight = false;
// ReSharper disable once InconsistentNaming
var panelLE = panelObj.AddComponent<LayoutElement>();
@@ -1830,8 +1863,8 @@ internal static class RenderConfigWindow
showActiveToggle
);
var titleTmp = card.Find("Header/TitleTrigger/TitleText")?.GetComponent<TMPro.TextMeshProUGUI>()
?? card.GetComponentsInChildren<TMPro.TextMeshProUGUI>(true).FirstOrDefault(t => t.gameObject.name == "TitleText");
var titleTmp = card.Find("Header/TitleTrigger/TitleText")?.GetComponent<TextMeshProUGUI>()
?? card.GetComponentsInChildren<TextMeshProUGUI>(true).FirstOrDefault(t => t.gameObject.name == "TitleText");
if (titleTmp)
{
titleTmp.fontSize = 18f;
@@ -1848,7 +1881,7 @@ internal static class RenderConfigWindow
/// <summary>
/// Creates a localized text label (default 13 pt, theme dim text color unless <paramref name="color"/> is given).
/// </summary>
private static TMPro.TextMeshProUGUI CreateLabel(
private static TextMeshProUGUI CreateLabel(
O5Context ctx,
Transform parent,
string textKey,
@@ -1960,7 +1993,7 @@ internal static class RenderConfigWindow
string? defaultValue,
string value,
string id,
TMPro.TMP_InputField.CharacterValidation characterValidation = TMPro.TMP_InputField.CharacterValidation.None,
TMP_InputField.CharacterValidation characterValidation = TMP_InputField.CharacterValidation.None,
Action<string>? onChanged = null)
{
return CreateInputField(ctx, colParent, labelKey, defaultValue, value, id, out _, characterValidation, onChanged);
@@ -1987,8 +2020,8 @@ internal static class RenderConfigWindow
string? defaultValue,
string value,
string id,
out TMPro.TextMeshProUGUI label,
TMPro.TMP_InputField.CharacterValidation characterValidation = TMPro.TMP_InputField.CharacterValidation.None,
out TextMeshProUGUI label,
TMP_InputField.CharacterValidation characterValidation = TMP_InputField.CharacterValidation.None,
Action<string>? onChanged = null)
{
// Labeled text input: an 18-unit label row (13 pt, dim) above an O5Kit input. The label key also serves as the
@@ -2026,28 +2059,48 @@ internal static class RenderConfigWindow
// ===================================================================================================
// Dependent field rules (rate control, codec)
// ===================================================================================================
/// <summary>
/// Blocks or unblocks an input and dims its label: a blocked label uses the theme dim text color at 40% of its
/// alpha. The label color changes with a 0.2-second tween (any running tween for that label is killed first), or
/// immediately with <paramref name="noAnimate"/>.
/// </summary>
/// <param name="input">Input to block; may be null.</param>
/// <param name="label">Label above the input; may be null.</param>
/// <param name="tweenHandle">Stored tween handle of this label, replaced by the new tween.</param>
/// <param name="blocked">True to block (disable) the field.</param>
/// <param name="noAnimate">True to apply the change without animation.</param>
private static void SetFieldBlocked(
O5InputField? input,
TMPro.TextMeshProUGUI? label,
TextMeshProUGUI? label,
ref ITweenHandle? tweenHandle,
bool blocked,
bool noAnimate = false)
{
input?.SetBlocked(blocked, noAnimate);
if (label == null) return;
UpdateLabelDim(label, ref tweenHandle, blocked, noAnimate);
}
private static void SetToggleBlocked(
O5Toggle? toggle,
TextMeshProUGUI? label,
ref ITweenHandle? tweenHandle,
bool blocked,
bool noAnimate = false)
{
toggle?.SetBlocked(blocked, noAnimate);
if (label == null) return;
UpdateLabelDim(label, ref tweenHandle, blocked, noAnimate);
}
private static void SetDropdownBlocked<T>(
O5Dropdown<T>? dropdown,
TextMeshProUGUI? label,
ref ITweenHandle? tweenHandle,
bool blocked,
bool noAnimate = false)
{
dropdown?.SetBlocked(blocked, noAnimate);
if (label == null) return;
UpdateLabelDim(label, ref tweenHandle, blocked, noAnimate);
}
private static void UpdateLabelDim(TextMeshProUGUI label, ref ITweenHandle? tweenHandle, bool blocked, bool noAnimate = false)
{
tweenHandle?.Kill();
var ctx = RendererUIManager.Context;
var baseColor = ctx.Theme.TextDim;
@@ -2055,41 +2108,20 @@ internal static class RenderConfigWindow
? new Color(baseColor.r, baseColor.g, baseColor.b, baseColor.a * 0.4f)
: baseColor;
if (noAnimate)
{
label.color = targetColor;
}
else
{
tweenHandle = ctx.Tween.TweenColor(label, targetColor, 0.2f);
}
if (noAnimate) label.color = targetColor;
else tweenHandle = ctx.Tween.TweenColor(label, targetColor, 0.2f);
}
/// <summary>
/// Enables only the video fields used by the rate control mode:
/// CBR - Bitrate; VBR - Bitrate and Max Bitrate; CQP - QP. Values of blocked fields stay in the config.
/// </summary>
private static void UpdateRateControlUI(RateControlMode mode, bool noAnimate = false)
{
bool blockBitrate = mode is RateControlMode.CQP;
bool blockMaxBitrate = mode is RateControlMode.CBR or RateControlMode.CQP;
bool blockQp = mode is RateControlMode.CBR or RateControlMode.VBR;
SetFieldBlocked(_bitrateInput, _bitrateLabel, ref _bitrateLabelTween, blockBitrate, noAnimate);
SetFieldBlocked(_maxBitrateInput, _maxBitrateLabel, ref _maxBitrateLabelTween, blockMaxBitrate, noAnimate);
SetFieldBlocked(_qpInput, _qpLabel, ref _qpLabelTween, blockQp, noAnimate);
}
/// <summary>
/// Replaces the sample rate choices with those supported by the audio codec paired with <paramref name="codec"/>.
/// Replaces the sample rate choices with those supported by the audio codec paired with <paramref name="videoCodec"/>.
/// O5Dropdown.SetValues keeps the current rate when it is in the new list and otherwise selects the first entry;
/// the resulting rate is written to <paramref name="config"/> when one is given.
/// </summary>
private static void UpdateVideoCodecUI(Codec codec, RenderConfig? config = null)
private static void UpdateSampleRateUI(Codec videoCodec, RenderConfig? config = null)
{
if (_sampleRateDropdown == null) return;
_sampleRateDropdown.SetValues(CodecUtil.AvailableSampleRate[CodecUtil.AudioCodecMatch[codec]]);
_sampleRateDropdown.SetValues(CodecUtil.AvailableSampleRate[CodecUtil.AudioCodecMatch[videoCodec]]);
if (config != null && _sampleRateDropdown.Value is var currentRate)
{
@@ -2097,6 +2129,35 @@ internal static class RenderConfigWindow
}
}
private static void UpdateUIBlock(RenderConfig config, bool noAnimate = false)
{
var blockCodecDropdown = false;
var blockRateControlModeDropdown = false;
var blockBitrateInput = config.VideoConfig.RateControlMode is RateControlMode.CQP;
var blockMaxBitrateInput = config.VideoConfig.RateControlMode is RateControlMode.CBR or RateControlMode.CQP;
// ReSharper disable once InconsistentNaming
var blockQPInput = config.VideoConfig.RateControlMode is RateControlMode.CBR or RateControlMode.VBR;
var blockForceSoftwareToggle = false;
var blockKeyframeIntervalSlider = false;
var blockVideoEncodingSettings = config.OutputMode is RenderConfig.RenderOutputMode.PNGSequence;
blockCodecDropdown |= blockVideoEncodingSettings;
blockRateControlModeDropdown |= blockVideoEncodingSettings;
blockBitrateInput |= blockVideoEncodingSettings;
blockMaxBitrateInput |= blockVideoEncodingSettings;
blockQPInput |= blockVideoEncodingSettings;
blockForceSoftwareToggle |= blockVideoEncodingSettings;
blockKeyframeIntervalSlider |= blockVideoEncodingSettings;
SetDropdownBlocked(_codecDropdown, _codecLabel, ref _codecLabelTween, blockCodecDropdown, noAnimate);
SetDropdownBlocked(_rateControlDropdown, _rateControlLabel, ref _rateControlLabelTween, blockRateControlModeDropdown, noAnimate);
SetFieldBlocked(_bitrateInput, _bitrateLabel, ref _bitrateLabelTween, blockBitrateInput, noAnimate);
SetFieldBlocked(_maxBitrateInput, _maxBitrateLabel, ref _maxBitrateLabelTween, blockMaxBitrateInput, noAnimate);
SetFieldBlocked(_qpInput, _qpLabel, ref _qpLabelTween, blockQPInput, noAnimate);
SetToggleBlocked(_forceSoftwareToggle, _forceSoftwareLabel, ref _forceSoftwareLabelTween, blockForceSoftwareToggle, noAnimate);
_keyframeIntervalSlider?.SetBlocked(blockKeyframeIntervalSlider, noAnimate);
}
// ===================================================================================================
// More control factory helpers
// ===================================================================================================
@@ -2154,6 +2215,21 @@ internal static class RenderConfigWindow
return input;
}
/// <summary>Same as the CreateToggleField overload with an <c>out</c> label parameter, without returning the label.</summary>
private static O5Toggle CreateToggleField(
O5Context ctx,
Transform colParent,
string labelKey,
bool? defaultValue,
bool value,
string toggleTextKey,
string id,
Action<bool>? onChanged = null,
bool generateLabel = true)
{
return CreateToggleField(ctx, colParent, labelKey, defaultValue, value, toggleTextKey, id, out _, onChanged, generateLabel);
}
/// <summary>
/// Creates an O5Kit toggle whose text is localized with <paramref name="toggleTextKey"/>. With
/// <paramref name="generateLabel"/> an 18-unit label row is added above it (text from <paramref name="labelKey"/>,
@@ -2167,12 +2243,14 @@ internal static class RenderConfigWindow
bool value,
string toggleTextKey,
string id,
out TextMeshProUGUI? label,
Action<bool>? onChanged = null,
bool generateLabel = true)
{
label = null;
if (generateLabel)
{
var label = O5Factory.ControlText(ctx, colParent, 13f, noPad: true);
label = O5Factory.ControlText(ctx, colParent, 13f, noPad: true);
label.text = string.IsNullOrEmpty(labelKey) ? "" : LocalizationManager.Get(labelKey);
label.color = ctx.Theme.TextDim;
LocalizedText.Bind(label, labelKey);
@@ -2200,6 +2278,21 @@ internal static class RenderConfigWindow
return toggle;
}
/// <summary>Same as the CreateDropdownField overload with an <c>out</c> label parameter, without returning the label.</summary>
private static O5Dropdown<T> CreateDropdownField<T>(
O5Context ctx,
Transform colParent,
string labelKey,
T defaultValue,
T value,
IReadOnlyList<T> values,
Func<T, string> display,
string id,
Action<T>? onChanged = null)
{
return CreateDropdownField(ctx, colParent, labelKey, defaultValue, value, values, display, id, out _, onChanged);
}
/// <summary>
/// Creates a labeled dropdown: an 18-unit label row and a container of preferred height 50 units holding the O5Kit
@@ -2215,9 +2308,10 @@ internal static class RenderConfigWindow
IReadOnlyList<T> values,
Func<T, string> display,
string id,
out TextMeshProUGUI label,
Action<T>? onChanged = null)
{
var label = O5Factory.ControlText(ctx, colParent, 13f, noPad: true);
label = O5Factory.ControlText(ctx, colParent, 13f, noPad: true);
label.text = LocalizationManager.Get(labelKey);
label.color = ctx.Theme.TextDim;
LocalizedText.Bind(label, labelKey);
@@ -2302,7 +2396,7 @@ internal static class RenderConfigWindow
if (header.Find("HexInput") is RectTransform hexInput)
{
hexInput.anchorMin = new Vector2(0.32f, 0f);
var tmpInput = hexInput.GetComponentInChildren<TMPro.TMP_InputField>();
var tmpInput = hexInput.GetComponentInChildren<TMP_InputField>();
if (tmpInput != null && tmpInput.textComponent != null)
{
tmpInput.textComponent.fontSize = 15f;
@@ -2313,6 +2407,103 @@ internal static class RenderConfigWindow
return picker;
}
/// <summary>
/// Creates a labeled segmented control field. When <paramref name="horizontal"/> is true,
/// places the label on the left and the segmented control on the right; otherwise stacks them vertically.
/// Both the label and segment buttons automatically track language changes via <see cref="LocalizedText"/>.
/// </summary>
private static SegmentedControl<T> CreateSegmentedField<T>(
O5Context ctx,
Transform parent,
string? labelKey,
T defaultValue,
T value,
IReadOnlyList<SegmentOption<T>> options,
string id,
Action<T>? onChanged = null,
bool horizontal = true,
float? height = null)
{
var controlHeight = height ?? ctx.Theme.ControlHeightFor(ctx.Theme.FontSizeBody);
var fieldObj = new GameObject($"Field_Seg_{id}");
fieldObj.transform.SetParent(parent, false);
var fieldRect = fieldObj.AddComponent<RectTransform>();
if (horizontal)
{
var hLayout = fieldObj.AddComponent<HorizontalLayoutGroup>();
hLayout.spacing = 16f;
hLayout.childAlignment = TextAnchor.MiddleLeft;
hLayout.childControlWidth = true;
hLayout.childControlHeight = true;
hLayout.childForceExpandWidth = false;
hLayout.childForceExpandHeight = false;
// ReSharper disable once InconsistentNaming
var fieldLE = fieldObj.AddComponent<LayoutElement>();
fieldLE.preferredHeight = controlHeight;
fieldLE.flexibleWidth = 1f;
if (!string.IsNullOrEmpty(labelKey))
{
var label = O5Factory.ControlText(ctx, fieldRect, ctx.Theme.FontSizeBody, noPad: true);
label.text = LocalizationManager.Get(labelKey);
label.color = ctx.Theme.TextDim;
LocalizedText.Bind(label, labelKey);
// ReSharper disable once InconsistentNaming
var labelLE = label.gameObject.AddComponent<LayoutElement>();
var csf = label.gameObject.AddComponent<ContentSizeFitter>();
csf.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
csf.verticalFit = ContentSizeFitter.FitMode.Unconstrained;
labelLE.flexibleWidth = 0f;
}
var segControl = SegmentedControl<T>.Create(ctx, fieldRect, id, defaultValue, value, options, controlHeight, onChanged);
// ReSharper disable once InconsistentNaming
var controlLE = segControl.Rect.GetComponent<LayoutElement>() ?? segControl.Rect.gameObject.AddComponent<LayoutElement>();
controlLE.flexibleWidth = 1f;
controlLE.preferredHeight = controlHeight;
return segControl;
}
else
{
var vLayout = fieldObj.AddComponent<VerticalLayoutGroup>();
vLayout.spacing = 3f;
vLayout.childControlWidth = true;
vLayout.childControlHeight = true;
vLayout.childForceExpandWidth = true;
vLayout.childForceExpandHeight = false;
// ReSharper disable once InconsistentNaming
var fieldLE = fieldObj.AddComponent<LayoutElement>();
fieldLE.flexibleWidth = 1f;
if (!string.IsNullOrEmpty(labelKey))
{
var label = O5Factory.ControlText(ctx, fieldRect, ctx.Theme.FontSizeBody, noPad: true);
label.text = LocalizationManager.Get(labelKey);
label.color = ctx.Theme.TextDim;
LocalizedText.Bind(label, labelKey);
// ReSharper disable once InconsistentNaming
var labelLE = label.gameObject.AddComponent<LayoutElement>();
labelLE.preferredHeight = 18f;
labelLE.minHeight = 18f;
}
var segControl = SegmentedControl<T>.Create(ctx, fieldRect, id, defaultValue, value, options, controlHeight, onChanged);
// ReSharper disable once InconsistentNaming
var controlLE = segControl.Rect.GetComponent<LayoutElement>() ?? segControl.Rect.gameObject.AddComponent<LayoutElement>();
controlLE.flexibleWidth = 1f;
controlLE.preferredHeight = controlHeight;
return segControl;
}
}
// ===================================================================================================
// Window visibility
// ===================================================================================================