512 lines
18 KiB
C#
512 lines
18 KiB
C#
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
|
|
{
|
|
/// <summary>
|
|
/// UI для настроек мода в меню настроек RimWorld
|
|
/// </summary>
|
|
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, 1250f);
|
|
|
|
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<FloatMenuOption> modelOptions = new List<FloatMenuOption>();
|
|
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<FloatMenuOption> styleOptions = new List<FloatMenuOption>();
|
|
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<FloatMenuOption> samplerOptions = new List<FloatMenuOption>();
|
|
var availableSamplers = settings.availableSamplers.Any()
|
|
? settings.availableSamplers
|
|
: new List<string> { 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<FloatMenuOption> schedulerOptions = new List<FloatMenuOption>();
|
|
var availableSchedulers = settings.availableSchedulers.Any()
|
|
? settings.availableSchedulers
|
|
: new List<string> { 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.CheckboxLabeled(
|
|
"AIImages.Settings.SaveImagesToServer".Translate(),
|
|
ref settings.saveImagesToServer,
|
|
"AIImages.Settings.SaveImagesToServerTooltip".Translate()
|
|
);
|
|
|
|
listingStandard.Gap(12f);
|
|
|
|
listingStandard.Label("AIImages.Settings.SavePath".Translate() + ":");
|
|
settings.savePath = listingStandard.TextEntry(settings.savePath);
|
|
|
|
listingStandard.Gap(12f);
|
|
|
|
// Кнопка очистки всех изображений
|
|
if (listingStandard.ButtonText("AIImages.Settings.ClearAllImages".Translate()))
|
|
{
|
|
ClearAllGeneratedImages(settings);
|
|
}
|
|
}
|
|
|
|
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<string> models,
|
|
List<string> samplers,
|
|
List<string> 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<string> availableValues,
|
|
string currentValue
|
|
)
|
|
{
|
|
bool needsSelection =
|
|
string.IsNullOrEmpty(currentValue) || !availableValues.Contains(currentValue);
|
|
|
|
if (needsSelection && availableValues.Count > 0)
|
|
{
|
|
selectedValue = availableValues[0];
|
|
}
|
|
}
|
|
|
|
private static void ClearAllGeneratedImages(AIImagesModSettings settings)
|
|
{
|
|
Find.WindowStack.Add(
|
|
Dialog_MessageBox.CreateConfirmation(
|
|
"AIImages.Settings.ClearAllImagesConfirm".Translate(),
|
|
delegate
|
|
{
|
|
int deletedCount = PawnPortraitHelper.ClearAllPortraits(settings.savePath);
|
|
Messages.Message(
|
|
"AIImages.Settings.ClearAllImagesSuccess".Translate(deletedCount),
|
|
MessageTypeDefOf.PositiveEvent
|
|
);
|
|
},
|
|
destructive: true
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|