using System;
using System.Collections.Generic;
using System.Linq;
using AIImages.Helpers;
using AIImages.Models;
using AIImages.Services;
using AIImages.Settings;
using RimWorld;
using UnityEngine;
using Verse;
namespace AIImages
{
///
/// UI для настроек мода в меню настроек RimWorld
///
public static class AIImagesSettingsUI
{
private static Vector2 scrollPosition = Vector2.zero;
private static string stepsBuffer;
private static string widthBuffer;
private static string heightBuffer;
public static void DoSettingsWindowContents(
Rect inRect,
AIImagesModSettings settings,
ServiceContainer serviceContainer
)
{
InitializeBuffers(settings);
Listing_Standard listingStandard = new Listing_Standard();
Rect viewRect = new Rect(0f, 0f, inRect.width - 20f, 1200f);
Widgets.BeginScrollView(inRect, ref scrollPosition, viewRect);
listingStandard.Begin(viewRect);
DrawApiSettings(listingStandard, settings, serviceContainer);
DrawGenerationSettings(listingStandard, settings);
DrawSamplerSchedulerSettings(listingStandard, settings);
DrawPromptsSettings(listingStandard, settings);
DrawOptionsSettings(listingStandard, settings);
listingStandard.End();
Widgets.EndScrollView();
}
private static void InitializeBuffers(AIImagesModSettings settings)
{
if (string.IsNullOrEmpty(stepsBuffer))
{
stepsBuffer = settings.steps.ToString();
widthBuffer = settings.width.ToString();
heightBuffer = settings.height.ToString();
}
}
private static void DrawApiSettings(
Listing_Standard listingStandard,
AIImagesModSettings settings,
ServiceContainer serviceContainer
)
{
listingStandard.Label(
"AIImages.Settings.ApiSection".Translate(),
-1f,
"AIImages.Settings.ApiSectionTooltip".Translate()
);
listingStandard.GapLine();
string oldEndpoint = settings.apiEndpoint;
listingStandard.Label("AIImages.Settings.ApiEndpoint".Translate() + ":");
settings.apiEndpoint = listingStandard.TextEntry(settings.apiEndpoint);
listingStandard.Gap(8f);
// Если endpoint изменился, пересоздаем API сервис
if (oldEndpoint != settings.apiEndpoint)
{
try
{
serviceContainer.RecreateApiService();
}
catch (Exception ex)
{
Log.Error($"[AI Images] Failed to recreate API service: {ex.Message}");
Messages.Message(
"Failed to update API endpoint. Check the log.",
MessageTypeDefOf.RejectInput
);
}
}
if (listingStandard.ButtonText("AIImages.Settings.TestConnection".Translate()))
{
TestApiConnection(serviceContainer.ApiService, settings.apiEndpoint);
}
if (listingStandard.ButtonText("AIImages.Settings.LoadFromApi".Translate()))
{
LoadAllFromApi(serviceContainer.ApiService, settings);
}
DrawModelDropdown(listingStandard, settings);
listingStandard.Gap(12f);
}
private static void DrawModelDropdown(
Listing_Standard listingStandard,
AIImagesModSettings settings
)
{
if (
listingStandard.ButtonTextLabeled(
"AIImages.Settings.Model".Translate(),
string.IsNullOrEmpty(settings.selectedModel)
? "AIImages.Settings.NoModelSelected".Translate()
: settings.selectedModel
)
)
{
List modelOptions = new List();
if (!settings.availableModels.Any())
{
modelOptions.Add(
new FloatMenuOption("AIImages.Settings.LoadModelsFirst".Translate(), null)
);
}
else
{
foreach (string model in settings.availableModels)
{
string localModel = model;
modelOptions.Add(
new FloatMenuOption(model, () => settings.selectedModel = localModel)
);
}
}
Find.WindowStack.Add(new FloatMenu(modelOptions));
}
}
private static void DrawGenerationSettings(
Listing_Standard listingStandard,
AIImagesModSettings settings
)
{
listingStandard.Label(
"AIImages.Settings.GenerationSection".Translate(),
-1f,
"AIImages.Settings.GenerationSectionTooltip".Translate()
);
listingStandard.GapLine();
DrawArtStyleDropdown(listingStandard, settings);
listingStandard.Gap(8f);
listingStandard.Label("AIImages.Settings.Steps".Translate() + $": {settings.steps}");
settings.steps = (int)listingStandard.Slider(settings.steps, 1, 150);
listingStandard.Gap(8f);
listingStandard.Label(
"AIImages.Settings.CfgScale".Translate() + $": {settings.cfgScale:F1}"
);
settings.cfgScale = listingStandard.Slider(settings.cfgScale, 1f, 30f);
listingStandard.Gap(8f);
DrawSizeSettings(listingStandard, settings);
listingStandard.Gap(12f);
}
private static void DrawArtStyleDropdown(
Listing_Standard listingStandard,
AIImagesModSettings settings
)
{
if (
listingStandard.ButtonTextLabeled(
"AIImages.Settings.ArtStyle".Translate(),
settings.artStyle.ToString()
)
)
{
List styleOptions = new List();
foreach (ArtStyle style in Enum.GetValues(typeof(ArtStyle)))
{
ArtStyle localStyle = style;
styleOptions.Add(
new FloatMenuOption(style.ToString(), () => settings.artStyle = localStyle)
);
}
Find.WindowStack.Add(new FloatMenu(styleOptions));
}
}
private static void DrawSizeSettings(
Listing_Standard listingStandard,
AIImagesModSettings settings
)
{
listingStandard.Label("AIImages.Settings.Width".Translate() + ":");
widthBuffer = listingStandard.TextEntry(widthBuffer);
if (int.TryParse(widthBuffer, out int width))
{
settings.width = Mathf.Clamp(width, 64, 2048);
}
listingStandard.Label("AIImages.Settings.Height".Translate() + ":");
heightBuffer = listingStandard.TextEntry(heightBuffer);
if (int.TryParse(heightBuffer, out int height))
{
settings.height = Mathf.Clamp(height, 64, 2048);
}
DrawSizePresets(listingStandard, settings);
}
private static void DrawSizePresets(
Listing_Standard listingStandard,
AIImagesModSettings settings
)
{
listingStandard.Gap(4f);
Rect presetRect1 = listingStandard.GetRect(30f);
DrawPresetButton(presetRect1, 0f, "512x512", 512, 512, settings);
DrawPresetButton(presetRect1, 85f, "512x768", 512, 768, settings);
DrawPresetButton(presetRect1, 170f, "768x768", 768, 768, settings);
listingStandard.Gap(4f);
Rect presetRect2 = listingStandard.GetRect(30f);
DrawPresetButton(presetRect2, 0f, "896x1152", 896, 1152, settings, 90f);
DrawPresetButton(presetRect2, 95f, "1024x1024", 1024, 1024, settings, 90f);
}
private static void DrawPresetButton(
Rect rect,
float xOffset,
string label,
int width,
int height,
AIImagesModSettings settings,
float buttonWidth = 80f
)
{
if (Widgets.ButtonText(new Rect(rect.x + xOffset, rect.y, buttonWidth, 30f), label))
{
settings.width = width;
settings.height = height;
widthBuffer = width.ToString();
heightBuffer = height.ToString();
}
}
private static void DrawSamplerSchedulerSettings(
Listing_Standard listingStandard,
AIImagesModSettings settings
)
{
DrawSamplerDropdown(listingStandard, settings);
DrawSchedulerDropdown(listingStandard, settings);
listingStandard.Gap(12f);
}
private static void DrawSamplerDropdown(
Listing_Standard listingStandard,
AIImagesModSettings settings
)
{
if (
listingStandard.ButtonTextLabeled(
"AIImages.Settings.Sampler".Translate(),
settings.selectedSampler
)
)
{
List samplerOptions = new List();
var availableSamplers = settings.availableSamplers.Any()
? settings.availableSamplers
: new List { settings.selectedSampler };
foreach (string sampler in availableSamplers)
{
string localSampler = sampler;
samplerOptions.Add(
new FloatMenuOption(sampler, () => settings.selectedSampler = localSampler)
);
}
Find.WindowStack.Add(new FloatMenu(samplerOptions));
}
}
private static void DrawSchedulerDropdown(
Listing_Standard listingStandard,
AIImagesModSettings settings
)
{
if (
listingStandard.ButtonTextLabeled(
"AIImages.Settings.Scheduler".Translate(),
settings.selectedScheduler
)
)
{
List schedulerOptions = new List();
var availableSchedulers = settings.availableSchedulers.Any()
? settings.availableSchedulers
: new List { settings.selectedScheduler };
foreach (string scheduler in availableSchedulers)
{
string localScheduler = scheduler;
schedulerOptions.Add(
new FloatMenuOption(
scheduler,
() => settings.selectedScheduler = localScheduler
)
);
}
Find.WindowStack.Add(new FloatMenu(schedulerOptions));
}
}
private static void DrawPromptsSettings(
Listing_Standard listingStandard,
AIImagesModSettings settings
)
{
listingStandard.Label(
"AIImages.Settings.PromptsSection".Translate(),
-1f,
"AIImages.Settings.PromptsSectionTooltip".Translate()
);
listingStandard.GapLine();
listingStandard.Label("AIImages.Settings.BasePositivePrompt".Translate() + ":");
settings.basePositivePrompt = listingStandard.TextEntry(settings.basePositivePrompt, 3);
listingStandard.Gap(8f);
listingStandard.Label("AIImages.Settings.BaseNegativePrompt".Translate() + ":");
settings.baseNegativePrompt = listingStandard.TextEntry(settings.baseNegativePrompt, 3);
listingStandard.Gap(12f);
}
private static void DrawOptionsSettings(
Listing_Standard listingStandard,
AIImagesModSettings settings
)
{
listingStandard.Label("AIImages.Settings.OptionsSection".Translate());
listingStandard.GapLine();
listingStandard.CheckboxLabeled(
"AIImages.Settings.AutoLoadModels".Translate(),
ref settings.autoLoadModels
);
listingStandard.CheckboxLabeled(
"AIImages.Settings.ShowTechnicalInfo".Translate(),
ref settings.showTechnicalInfo
);
listingStandard.CheckboxLabeled(
"AIImages.Settings.SaveHistory".Translate(),
ref settings.saveGenerationHistory
);
listingStandard.Gap(12f);
listingStandard.Label("AIImages.Settings.SavePath".Translate() + ":");
settings.savePath = listingStandard.TextEntry(settings.savePath);
}
private static void TestApiConnection(
IStableDiffusionApiService apiService,
string endpoint
)
{
_ = AsyncHelper.RunAsync(
async () =>
{
Log.Message($"[AI Images] Testing connection to {endpoint}...");
bool available = await apiService.CheckApiAvailability(endpoint);
if (available)
{
Messages.Message(
"AIImages.Settings.ConnectionSuccess".Translate(),
MessageTypeDefOf.PositiveEvent
);
}
else
{
Messages.Message(
"AIImages.Settings.ConnectionFailed".Translate(),
MessageTypeDefOf.RejectInput
);
}
},
"API Connection Test"
);
}
private static void LoadAllFromApi(
IStableDiffusionApiService apiService,
AIImagesModSettings settings
)
{
_ = AsyncHelper.RunAsync(
async () =>
{
Log.Message("[AI Images] Loading models, samplers and schedulers from API...");
// Загружаем модели
var models = await apiService.GetAvailableModels(settings.apiEndpoint);
settings.availableModels = models;
// Загружаем семплеры
var samplers = await apiService.GetAvailableSamplers(settings.apiEndpoint);
settings.availableSamplers = samplers;
// Загружаем schedulers
var schedulers = await apiService.GetAvailableSchedulers(settings.apiEndpoint);
settings.availableSchedulers = schedulers;
int totalCount = models.Count + samplers.Count + schedulers.Count;
if (totalCount > 0)
{
ShowSuccessMessage(models.Count, samplers.Count, schedulers.Count);
AutoSelectDefaults(settings, models, samplers, schedulers);
}
else
{
Messages.Message(
"AIImages.Settings.NothingLoaded".Translate(),
MessageTypeDefOf.RejectInput
);
}
},
"Load API Data"
);
}
private static void ShowSuccessMessage(int modelCount, int samplerCount, int schedulerCount)
{
Messages.Message(
"AIImages.Settings.AllLoaded".Translate(modelCount, samplerCount, schedulerCount),
MessageTypeDefOf.PositiveEvent
);
}
private static void AutoSelectDefaults(
AIImagesModSettings settings,
List models,
List samplers,
List schedulers
)
{
AutoSelectIfNeeded(ref settings.selectedModel, models, settings.selectedModel);
AutoSelectIfNeeded(ref settings.selectedSampler, samplers, settings.selectedSampler);
AutoSelectIfNeeded(
ref settings.selectedScheduler,
schedulers,
settings.selectedScheduler
);
}
private static void AutoSelectIfNeeded(
ref string selectedValue,
List availableValues,
string currentValue
)
{
bool needsSelection =
string.IsNullOrEmpty(currentValue) || !availableValues.Contains(currentValue);
if (needsSelection && availableValues.Count > 0)
{
selectedValue = availableValues[0];
}
}
}
}