diff --git a/YAFC/Widgets/DataGrid.cs b/YAFC/Widgets/DataGrid.cs index 2752ac2d..f31b58c4 100644 --- a/YAFC/Widgets/DataGrid.cs +++ b/YAFC/Widgets/DataGrid.cs @@ -40,18 +40,18 @@ public override void BuildHeader(ImGui gui) if (hasMenu) { var rect = gui.statePosition; - var menuRect = new Rect(rect.Right-1.7f, rect.Y, 1.5f, 1.5f); + var menuRect = new Rect(rect.Right - 1.7f, rect.Y, 1.5f, 1.5f); if (gui.isBuilding) gui.DrawIcon(menuRect, Icon.DropDown, SchemeColor.BackgroundText); if (gui.BuildButton(menuRect, SchemeColor.None, SchemeColor.Grey)) gui.ShowDropDown(menuRect, BuildMenu, new Padding(1f)); } } - - public virtual void BuildMenu(ImGui gui) {} + + public virtual void BuildMenu(ImGui gui) { } } - public class DataGrid where TData:class + public class DataGrid where TData : class { public readonly List> columns; private readonly Padding innerPadding = new Padding(0.2f); @@ -66,7 +66,7 @@ public DataGrid(params DataColumn[] columns) this.columns = new List>(columns); spacing = innerPadding.left + innerPadding.right; } - + private void BuildHeaderResizer(ImGui gui, DataColumn column, Rect rect) { @@ -89,7 +89,7 @@ private void BuildHeaderResizer(ImGui gui, DataColumn column, Rect rect) gui.Rebuild(); break; case ImGuiAction.MouseDown: - gui.ConsumeMouseDown(rect, cursor:RenderingUtils.cursorHorizontalResize); + gui.ConsumeMouseDown(rect, cursor: RenderingUtils.cursorHorizontalResize); break; case ImGuiAction.MouseUp: if (gui.ConsumeMouseUp(rect, false)) @@ -102,6 +102,16 @@ private void BuildHeaderResizer(ImGui gui, DataColumn column, Rect rect) } } + private void CalculateWidth(ImGui gui) + { + var x = 0f; + foreach (var column in columns) + { + x += column.width + spacing; + } + width = MathF.Max(x + 0.2f - spacing, gui.width - 1f); + } + public void BuildHeader(ImGui gui) { var spacing = innerPadding.left + innerPadding.right; @@ -127,7 +137,7 @@ public void BuildHeader(ImGui gui) } } } - width = MathF.Max(x + 0.2f - spacing, gui.width - 1f); + CalculateWidth(gui); var separator = gui.AllocateRect(x, 0.1f); if (gui.isBuilding) @@ -162,10 +172,11 @@ public Rect BuildRow(ImGui gui, TData element, float startX = 0f) buildGroup.Complete(); } + CalculateWidth(gui); var rect = gui.lastRect; var bottom = gui.lastRect.Bottom; if (gui.isBuilding) - gui.DrawRectangle(new Rect(startX, bottom - 0.1f, width-startX, 0.1f), SchemeColor.Grey); + gui.DrawRectangle(new Rect(startX, bottom - 0.1f, width - startX, 0.1f), SchemeColor.Grey); return rect; } @@ -178,7 +189,7 @@ public void BeginBuildingContent(ImGui gui) public Rect EndBuildingContent(ImGui gui) { var bottom = gui.statePosition.Bottom; - return new Rect(buildingStart.X, buildingStart.Y, width, bottom-buildingStart.Y); + return new Rect(buildingStart.X, buildingStart.Y, width, bottom - buildingStart.Y); } public bool BuildContent(ImGui gui, IReadOnlyList data, out (TData from, TData to) reorder, out Rect rect, Func filter = null) diff --git a/YAFC/Widgets/ImmediateWidgets.cs b/YAFC/Widgets/ImmediateWidgets.cs index b120a606..c27a5bef 100644 --- a/YAFC/Widgets/ImmediateWidgets.cs +++ b/YAFC/Widgets/ImmediateWidgets.cs @@ -66,8 +66,10 @@ public static bool BuildFactorioObjectButton(this ImGui gui, Rect rect, Factorio else { overColor = bgColor + 1; - if (MainScreen.Instance.IsSameObjectHovered(gui, obj)) - bgColor = overColor; + } + if (MainScreen.Instance.IsSameObjectHovered(gui, obj)) + { + bgColor = overColor; } var evt = gui.BuildButton(rect, bgColor, overColor, button: 0); if (evt == ButtonEvent.MouseOver && obj != null) diff --git a/YAFC/Widgets/ObjectTooltip.cs b/YAFC/Widgets/ObjectTooltip.cs index f1303c22..0f379d38 100644 --- a/YAFC/Widgets/ObjectTooltip.cs +++ b/YAFC/Widgets/ObjectTooltip.cs @@ -432,7 +432,7 @@ private void BuildRecipe(RecipeOrTechnology recipe, ImGui gui) gui.BuildText(DataUtils.FormatAmount(ingredient.amount, UnitOfMeasure.None)); } - gui.allocator = RectAllocator.RemainigRow; + gui.allocator = RectAllocator.RemainingRow; gui.BuildFactorioObjectButtonWithText(technology); } } diff --git a/YAFC/Windows/DependencyExplorer.cs b/YAFC/Windows/DependencyExplorer.cs index 7b38505c..571c7ae3 100644 --- a/YAFC/Windows/DependencyExplorer.cs +++ b/YAFC/Windows/DependencyExplorer.cs @@ -10,11 +10,11 @@ namespace YAFC public class DependencyExplorer : PseudoScreen { private static readonly DependencyExplorer Instance = new DependencyExplorer(); - - private readonly VerticalScrollCustom dependencies; - private readonly VerticalScrollCustom dependants; + + private readonly ScrollArea dependencies; + private readonly ScrollArea dependants; private static readonly Padding listPad = new Padding(0.5f); - + private readonly List history = new List(); private FactorioObject current; @@ -31,11 +31,11 @@ public class DependencyExplorer : PseudoScreen {DependencyList.Flags.SourceEntity, ("Source", "This recipe requires another entity")}, {DependencyList.Flags.Hidden, ("", "This technology is hidden")}, }; - + public DependencyExplorer() : base(60f) { - dependencies = new VerticalScrollCustom(30f, DrawDependencies); - dependants = new VerticalScrollCustom(30f, DrawDependants); + dependencies = new ScrollArea(30f, DrawDependencies); + dependants = new ScrollArea(30f, DrawDependants); } public static void Show(FactorioObject target) @@ -52,9 +52,9 @@ private void DrawFactorioObject(ImGui gui, FactorioId id) { gui.BuildFactorioObjectIcon(fobj); var text = fobj.locName + " (" + fobj.type + ")"; - gui.RemainingRow(0.5f).BuildText(text, null, true, color:fobj.IsAccessible() ? SchemeColor.BackgroundText : SchemeColor.BackgroundTextFaint); + gui.RemainingRow(0.5f).BuildText(text, null, true, color: fobj.IsAccessible() ? SchemeColor.BackgroundText : SchemeColor.BackgroundTextFaint); } - if (gui.BuildFactorioObjectButton(gui.lastRect, fobj, extendHeader:true)) + if (gui.BuildFactorioObjectButton(gui.lastRect, fobj, extendHeader: true)) Change(fobj); } @@ -64,12 +64,12 @@ private void DrawDependencies(ImGui gui) foreach (var data in Dependencies.dependencyList[current]) { if (!dependencyListTexts.TryGetValue(data.flags, out var dependencyType)) - dependencyType = (data.flags.ToString(), "Missing "+data.flags); + dependencyType = (data.flags.ToString(), "Missing " + data.flags); if (data.elements.Length > 0) { gui.AllocateSpacing(0.5f); if (data.elements.Length == 1) - gui.BuildText("Require this "+dependencyType.name+":"); + gui.BuildText("Require this " + dependencyType.name + ":"); else if (data.flags.HasFlags(DependencyList.Flags.RequireEverything)) gui.BuildText("Require ALL of these " + dependencyType.name + "s:"); else gui.BuildText("Require ANY of these " + dependencyType.name + "s:"); @@ -83,7 +83,7 @@ private void DrawDependencies(ImGui gui) if (Database.rootAccessible.Contains(current)) text += ", but it is inherently accessible"; else text += ", and it is inaccessible"; - gui.BuildText(text, wrap:true); + gui.BuildText(text, wrap: true); } } } @@ -113,7 +113,7 @@ public override void Build(ImGui gui) gui.BuildText("Currently inspecting:", Font.subheader); if (gui.BuildFactorioObjectButtonWithText(current)) SelectObjectPanel.Select(Database.objects.all, "Select something", Change); - gui.BuildText("(Click to change)", color:SchemeColor.BackgroundTextFaint); + gui.BuildText("(Click to change)", color: SchemeColor.BackgroundTextFaint); } using (gui.EnterRow()) { @@ -123,7 +123,7 @@ public override void Build(ImGui gui) if (current.IsAutomatable()) gui.BuildText("Status: Automatable"); else gui.BuildText("Status: Accessible, Not automatable"); - + if (settings.Flags(current).HasFlags(ProjectPerItemFlags.MarkedAccessible)) { gui.BuildText("Manually marked as accessible."); @@ -165,7 +165,7 @@ public override void Build(ImGui gui) dependants.Build(gui); } } - + public void Change(FactorioObject target) { if (target == null) @@ -174,7 +174,7 @@ public void Change(FactorioObject target) Close(); return; } - + history.Add(current); if (history.Count > 100) history.RemoveRange(0, 20); @@ -190,7 +190,7 @@ public override bool KeyDown(SDL.SDL_Keysym key) { var last = history[history.Count - 1]; Change(last); - history.RemoveRange(history.Count-2, 2); + history.RemoveRange(history.Count - 2, 2); return true; } return base.KeyDown(key); diff --git a/YAFC/Windows/ErrorListPanel.cs b/YAFC/Windows/ErrorListPanel.cs index 68f16216..f7f48f82 100644 --- a/YAFC/Windows/ErrorListPanel.cs +++ b/YAFC/Windows/ErrorListPanel.cs @@ -7,19 +7,19 @@ public class ErrorListPanel : PseudoScreen { private static readonly ErrorListPanel Instance = new ErrorListPanel(); private ErrorCollector collector; - private readonly VerticalScrollCustom verticalList; + private readonly ScrollArea verticalList; private (string error, ErrorSeverity severity)[] errors; public ErrorListPanel() : base(60f) { - verticalList = new VerticalScrollCustom(30f, BuildErrorList, default, true); + verticalList = new ScrollArea(30f, BuildErrorList, default, true); } private void BuildErrorList(ImGui gui) { foreach (var error in errors) { - gui.BuildText(error.error, wrap:true, color:error.severity >= ErrorSeverity.MajorDataLoss ? SchemeColor.Error : SchemeColor.BackgroundText); + gui.BuildText(error.error, wrap: true, color: error.severity >= ErrorSeverity.MajorDataLoss ? SchemeColor.Error : SchemeColor.BackgroundText); } } @@ -37,7 +37,7 @@ public override void Build(ImGui gui) BuildHeader(gui, "Loading completed with errors"); else BuildHeader(gui, "Analysis warnings"); verticalList.Build(gui); - + } } } \ No newline at end of file diff --git a/YAFC/Windows/MainScreen.cs b/YAFC/Windows/MainScreen.cs index 45668085..124de2be 100644 --- a/YAFC/Windows/MainScreen.cs +++ b/YAFC/Windows/MainScreen.cs @@ -14,6 +14,8 @@ namespace YAFC { public class MainScreen : WindowMain, IKeyboardFocus, IProgress<(string, string)> { + public static readonly Guid SummaryGuid = Guid.Parse("9bdea333-4be2-4be3-b708-b36a64672a40"); + public static MainScreen Instance { get; private set; } private readonly ObjectTooltip objectTooltip = new ObjectTooltip(); private readonly List pseudoScreens = new List(); @@ -30,7 +32,8 @@ public class MainScreen : WindowMain, IKeyboardFocus, IProgress<(string, string) private ProjectPage _secondaryPage; public ProjectPage secondaryPage => _secondaryPage; private ProjectPageView secondaryPageView; - + private readonly SummaryView summaryView; + private bool analysisUpdatePending; private SearchQuery pageSearch; private SearchQuery pageListSearch; @@ -43,14 +46,19 @@ public class MainScreen : WindowMain, IKeyboardFocus, IProgress<(string, string) public MainScreen(int display, Project project) : base(default) { + summaryView = new SummaryView(); RegisterPageView(new ProductionTableView()); RegisterPageView(new AutoPlannerView()); RegisterPageView(new ProductionSummaryView()); - searchGui = new ImGui(BuildSearch, new Padding(1f)) {boxShadow = RectangleBorder.Thin, boxColor = SchemeColor.Background}; + RegisterPageView(summaryView); + // HACK: SummaryView does not like it to be cloned to the cloned as a secondary page, probably due to its contents (type) not set properly? + // Manually set SummaryView as a secondary page type with the correct key (type) + secondaryPageViews[typeof(Summary)] = summaryView; + searchGui = new ImGui(BuildSearch, new Padding(1f)) { boxShadow = RectangleBorder.Thin, boxColor = SchemeColor.Background }; Instance = this; tabBar = new MainScreenTabBar(this); - allPages = new VirtualScrollList(30, new Vector2(0f, 2f), BuildPage, collapsible:true); - Create("Yet Another Factorio Calculator v"+YafcLib.version, display); + allPages = new VirtualScrollList(30, new Vector2(0f, 2f), BuildPage, collapsible: true); + Create("Yet Another Factorio Calculator v" + YafcLib.version, display); SetProject(project); } @@ -71,17 +79,28 @@ private void SetProject(Project project) if (project.pages.Count == 0) { + var firstPage = new ProjectPage(project, typeof(ProductionTable)); project.pages.Add(firstPage); } if (project.displayPages.Count == 0) + { project.displayPages.Add(project.pages[0].guid); - + } + + // Hack to activate all page solvers for the summary view + foreach (var page in project.pages) + { + page.SetActive(true); + page.SetActive(false); + } + SetActivePage(project.FindPage(project.displayPages[0])); project.metaInfoChanged += ProjectOnMetaInfoChanged; project.settings.changed += ProjectSettingsChanged; InputSystem.Instance.SetDefaultKeyboardFocus(this); + summaryView.SetProject(project); } private void ProjectSettingsChanged(bool visualOnly) @@ -109,9 +128,9 @@ private void BuildPage(ImGui gui, ProjectPage element, int index) { if (element.icon != null) gui.BuildIcon(element.icon.icon); - gui.RemainingRow().BuildText(element.name, color:element.visible ? SchemeColor.BackgroundText : SchemeColor.BackgroundTextFaint); + gui.RemainingRow().BuildText(element.name, color: element.visible ? SchemeColor.BackgroundText : SchemeColor.BackgroundTextFaint); } - var evt = gui.BuildButton(gui.lastRect, SchemeColor.PureBackground, SchemeColor.Grey, button:0); + var evt = gui.BuildButton(gui.lastRect, SchemeColor.PureBackground, SchemeColor.Grey, button: 0); if (evt) { if (gui.actionParameter == SDL.SDL_BUTTON_MIDDLE) @@ -178,7 +197,7 @@ public void RegisterPageView(ProjectPageView pageView) where T : ProjectPageC { registeredPageViews[typeof(T)] = pageView; } - + public void RebuildProjectView() { rootGui.MarkEverythingForRebuild(); @@ -189,7 +208,7 @@ public void RebuildProjectView() } protected override void BuildContent(ImGui gui) - { + { if (pseudoScreens.Count > 0) { var top = pseudoScreens[0]; @@ -270,12 +289,13 @@ private void BuildPage(ImGui gui) vsize.Y /= 2f; _activePageView.Build(gui, vsize); secondaryPageView.Build(gui, vsize); - } else + } + else _activePageView.Build(gui, pageVisibleSize); if (pageSearch.query != null && gui.isBuilding) { var searchSize = searchGui.CalculateState(30, gui.pixelsPerUnit); - gui.DrawPanel(new Rect(pageVisibleSize.X-searchSize.X, usedHeaderSpace, searchSize.X, searchSize.Y), searchGui); + gui.DrawPanel(new Rect(pageVisibleSize.X - searchSize.X, usedHeaderSpace, searchSize.X, searchSize.Y), searchGui); } } else @@ -287,10 +307,10 @@ private void BuildPage(ImGui gui) } } } - + public ProjectPage AddProjectPage(string name, FactorioObject icon, Type contentType, bool setActive, bool initNew) { - var page = new ProjectPage(project, contentType) {name = name, icon = icon}; + var page = new ProjectPage(project, contentType) { name = name, icon = icon }; if (initNew) page.content.InitNew(); project.RecordUndo().pages.Add(page); @@ -320,7 +340,7 @@ private void MissingPagesDropdown(ImGui gui) } allPages.Build(gui); } - + public void BuildSubHeader(ImGui gui, string text) { using (gui.EnterGroup(ObjectTooltip.contentPadding)) @@ -370,7 +390,7 @@ private void BuildSearch(ImGui gui) private void SettingsDropdown(ImGui gui) { gui.boxColor = SchemeColor.Background; - if (gui.BuildContextMenuButton("Undo", "Ctrl+" +ImGuiUtils.ScanToString(SDL.SDL_Scancode.SDL_SCANCODE_Z)) && gui.CloseDropdown()) + if (gui.BuildContextMenuButton("Undo", "Ctrl+" + ImGuiUtils.ScanToString(SDL.SDL_Scancode.SDL_SCANCODE_Z)) && gui.CloseDropdown()) project.undo.PerformUndo(); if (gui.BuildContextMenuButton("Save", "Ctrl+" + ImGuiUtils.ScanToString(SDL.SDL_Scancode.SDL_SCANCODE_S)) && gui.CloseDropdown()) SaveProject().CaptureException(); @@ -389,22 +409,25 @@ private void SettingsDropdown(ImGui gui) if (gui.BuildContextMenuButton("Preferences") && gui.CloseDropdown()) PreferencesScreen.Show(); + if (gui.BuildContextMenuButton("Summary") && gui.CloseDropdown()) + ShowSummaryTab(); + if (gui.BuildContextMenuButton("Never Enough Items Explorer", "Ctrl+" + ImGuiUtils.ScanToString(SDL.SDL_Scancode.SDL_SCANCODE_N)) && gui.CloseDropdown()) ShowNeie(); if (gui.BuildContextMenuButton("Dependency Explorer") && gui.CloseDropdown()) SelectObjectPanel.Select(Database.objects.all, "Open Dependency Explorer", DependencyExplorer.Show); - + BuildSubHeader(gui, "Extra"); if (gui.BuildContextMenuButton("Run Factorio")) { var factorioPath = DataUtils.dataPath + "/../bin/x64/factorio"; var args = string.IsNullOrEmpty(DataUtils.modsPath) ? null : "--mod-directory \"" + DataUtils.modsPath + "\""; - Process.Start(new ProcessStartInfo(factorioPath, args) {UseShellExecute = true}); + Process.Start(new ProcessStartInfo(factorioPath, args) { UseShellExecute = true }); gui.CloseDropdown(); } - + if (gui.BuildContextMenuButton("Check for updates") && gui.CloseDropdown()) DoCheckForUpdates(); @@ -501,6 +524,22 @@ public bool ShowPseudoScreen(PseudoScreen screen) return true; } + public void ShowSummaryTab() + { + + var summaryPage = project.FindPage(SummaryGuid); + if (summaryPage == null) + { + + summaryPage = new ProjectPage(project, typeof(Summary), SummaryGuid); + summaryPage.name = "Summary"; + project.pages.Add(summaryPage); + // project.displayPages.Add(summaryPage.guid); + } + + SetActivePage(summaryPage); + } + public void ClosePseudoScreen(PseudoScreen screen) { pseudoScreens.Remove(screen); @@ -523,12 +562,14 @@ public bool KeyDown(SDL.SDL_Keysym key) else project.undo.PerformUndo(); _activePageView?.Rebuild(false); secondaryPageView?.Rebuild(false); - } else if (key.scancode == SDL.SDL_Scancode.SDL_SCANCODE_Y) + } + else if (key.scancode == SDL.SDL_Scancode.SDL_SCANCODE_Y) { project.undo.PerformRedo(); _activePageView?.Rebuild(false); secondaryPageView?.Rebuild(false); - } else if (key.scancode == SDL.SDL_Scancode.SDL_SCANCODE_N) + } + else if (key.scancode == SDL.SDL_Scancode.SDL_SCANCODE_N) ShowNeie(); else if (key.scancode == SDL.SDL_Scancode.SDL_SCANCODE_F) ShowSearch(); @@ -568,7 +609,7 @@ private Task SaveProject() return SaveProjectAs(); } - + private async void LoadProjectLight() { if (project.unsavedChangesCount > 0 && !await ConfirmUnsavedChanges()) @@ -604,9 +645,9 @@ private async void LoadProjectHeavy() public bool TextInput(string input) => true; public bool KeyUp(SDL.SDL_Keysym key) => true; - public void FocusChanged(bool focused) {} + public void FocusChanged(bool focused) { } private new void MainRender() => base.MainRender(); - + private class FadeDrawer : IRenderable { private SDL.SDL_Rect srcRect; @@ -621,8 +662,8 @@ public void CreateDownscaledImage() Instance.surface.EndRenderToTexture(); for (var i = 0; i < 2; i++) { - var halfSize = new SDL.SDL_Rect() {w = size.w/2, h = size.h/2}; - var halfTexture = Instance.surface.CreateTexture(SDL.SDL_PIXELFORMAT_RGBA8888, (int) SDL.SDL_TextureAccess.SDL_TEXTUREACCESS_TARGET, halfSize.w, halfSize.h); + var halfSize = new SDL.SDL_Rect() { w = size.w / 2, h = size.h / 2 }; + var halfTexture = Instance.surface.CreateTexture(SDL.SDL_PIXELFORMAT_RGBA8888, (int)SDL.SDL_TextureAccess.SDL_TEXTUREACCESS_TARGET, halfSize.w, halfSize.h); SDL.SDL_SetRenderTarget(renderer, halfTexture.handle); var bgColor = SchemeColor.PureBackground.ToSdlColor(); SDL.SDL_SetRenderDrawColor(renderer, bgColor.r, bgColor.g, bgColor.b, bgColor.a); @@ -661,7 +702,7 @@ public void ShowTooltip(ImGui gui, ProjectPage page, bool isMiddleEdit, Rect rec { pageView.BuildPageTooltip(x, page.content); if (isMiddleEdit) - x.BuildText("Middle mouse button to edit", Font.text, true, color:SchemeColor.BackgroundTextFaint); + x.BuildText("Middle mouse button to edit", Font.text, true, color: SchemeColor.BackgroundTextFaint); }); } } diff --git a/YAFC/Windows/NeverEnoughItemsPanel.cs b/YAFC/Windows/NeverEnoughItemsPanel.cs index 5ae0f998..43731e69 100644 --- a/YAFC/Windows/NeverEnoughItemsPanel.cs +++ b/YAFC/Windows/NeverEnoughItemsPanel.cs @@ -6,7 +6,7 @@ namespace YAFC { public class NeverEnoughItemsPanel : PseudoScreen, IComparer { - private static readonly NeverEnoughItemsPanel Instance = new NeverEnoughItemsPanel(); + private static readonly NeverEnoughItemsPanel Instance = new NeverEnoughItemsPanel(); private Goods current; private Goods changing; private float currentFlow; @@ -14,9 +14,9 @@ public class NeverEnoughItemsPanel : PseudoScreen, IComparer recent = new List(); private bool atCurrentMilestones; - private readonly VerticalScrollCustom productionList; - private readonly VerticalScrollCustom usageList; - + private readonly ScrollArea productionList; + private readonly ScrollArea usageList; + private enum EntryStatus { NotAccessible, @@ -25,7 +25,7 @@ private enum EntryStatus Normal, Useful } - + private readonly struct RecipeEntry { public readonly Recipe recipe; @@ -59,11 +59,11 @@ public RecipeEntry(Recipe recipe, bool isProduction, Goods currentItem, bool atC private readonly List productions = new List(); private readonly List usages = new List(); - + public NeverEnoughItemsPanel() : base(76f) { - productionList = new VerticalScrollCustom(40f, BuildItemProduction, new Padding(0.5f)); - usageList = new VerticalScrollCustom(40f, BuildItemUsages, new Padding(0.5f)); + productionList = new ScrollArea(40f, BuildItemProduction, new Padding(0.5f)); + usageList = new ScrollArea(40f, BuildItemUsages, new Padding(0.5f)); } private void SetItem(Goods current) @@ -79,7 +79,7 @@ private void SetItem(Goods current) currentFlow = current.ApproximateFlow(atCurrentMilestones); productions.Clear(); foreach (var recipe in current.production) - productions.Add(new RecipeEntry(recipe, true, current, atCurrentMilestones)); + productions.Add(new RecipeEntry(recipe, true, current, atCurrentMilestones)); productions.Sort(this); usages.Clear(); foreach (var usage in current.usages) @@ -157,7 +157,8 @@ private void DrawRecipeEntry(ImGui gui, RecipeEntry entry, bool production) { bgColor = SchemeColor.None; textcolor = SchemeColor.BackgroundTextFaint; - } else if (entry.flow > 0f) + } + else if (entry.flow > 0f) { bgColor = SchemeColor.Secondary; textcolor = SchemeColor.SecondaryText; @@ -172,12 +173,12 @@ private void DrawRecipeEntry(ImGui gui, RecipeEntry entry, bool production) using (gui.EnterRow()) { gui.BuildIcon(Icon.Time); - gui.BuildText(DataUtils.FormatAmount(entry.recipe.time, UnitOfMeasure.Second), align:RectAlignment.Middle); + gui.BuildText(DataUtils.FormatAmount(entry.recipe.time, UnitOfMeasure.Second), align: RectAlignment.Middle); } var bh = CostAnalysis.Instance.GetBuildingHours(recipe, entry.recipeFlow); if (bh > 20) { - gui.BuildText(DataUtils.FormatAmount(bh, UnitOfMeasure.None, suffix:"bh"), align:RectAlignment.Middle); + gui.BuildText(DataUtils.FormatAmount(bh, UnitOfMeasure.None, suffix: "bh"), align: RectAlignment.Middle); gui.BuildButton(gui.lastRect, SchemeColor.None, SchemeColor.Grey).WithTooltip(gui, "Building-hours.\nAmount of building-hours required for all researches assuming crafting speed of 1"); } } @@ -192,7 +193,7 @@ private void DrawRecipeEntry(ImGui gui, RecipeEntry entry, bool production) if (gui.BuildButton(iconRect, SchemeColor.None, SchemeColor.BackgroundAlt)) Project.current.preferences.ToggleFavourite(entry.recipe); gui.allocator = textalloc; - gui.BuildText(recipe.locName, wrap:true); + gui.BuildText(recipe.locName, wrap: true); } if (recipe.ingredients.Length + recipe.products.Length <= 8) { @@ -204,7 +205,7 @@ private void DrawRecipeEntry(ImGui gui, RecipeEntry entry, bool production) if (recipe.products.Length < 3 && recipe.ingredients.Length < 5) gui.AllocateSpacing((3 - entry.recipe.products.Length) * 3f); else if (recipe.products.Length < 3) - gui.allocator = RectAllocator.RemainigRow; + gui.allocator = RectAllocator.RemainingRow; gui.BuildIcon(Icon.ArrowRight, 3f); } } @@ -230,7 +231,8 @@ private void DrawRecipeEntry(ImGui gui, RecipeEntry entry, bool production) var percentFlow = MathUtils.Clamp(entry.flow / currentFlow, 0f, 1f); rect.Width *= percentFlow; gui.DrawRectangle(rect, SchemeColor.Primary); - } else if (waste <= 0f) + } + else if (waste <= 0f) bgColor = SchemeColor.Secondary; else { @@ -247,7 +249,7 @@ private void DrawEntryFooter(ImGui gui, bool production) { using (gui.EnterGroup(new Padding(0.5f), RectAllocator.LeftAlign)) { - gui.BuildText(current.fuelValue > 0f ? "Fuel value "+DataUtils.FormatAmount(current.fuelValue, UnitOfMeasure.Megajoule)+" can be used for:" : "Can be used to fuel:"); + gui.BuildText(current.fuelValue > 0f ? "Fuel value " + DataUtils.FormatAmount(current.fuelValue, UnitOfMeasure.Megajoule) + " can be used for:" : "Can be used to fuel:"); using (var grid = gui.EnterInlineGrid(3f)) { foreach (var fuelUsage in current.fuelFor) @@ -285,12 +287,12 @@ private void DrawEntryList(ImGui gui, List entries, bool production footerDrawn = true; gui.BuildText(entry.entryStatus == EntryStatus.Special ? "Show special recipes (barreling / voiding)" : entry.entryStatus == EntryStatus.NotAccessibleWithCurrentMilestones ? "There are more recipes, but they are locked based on current milestones" : - "There are more recipes but they are inaccessible", wrap:true); + "There are more recipes but they are inaccessible", wrap: true); if (gui.BuildButton("Show more recipes")) ChangeShowStatus(status); break; } - + if (status < prevEntryStatus) { prevEntryStatus = status; @@ -307,7 +309,7 @@ private void DrawEntryList(ImGui gui, List entries, bool production var latest = Milestones.Instance.GetHighest(entry.recipe, false); if (latest != prevLatestMilestone) { - gui.BuildFactorioObjectButtonWithText(latest, size:3f, display:MilestoneDisplay.None); + gui.BuildFactorioObjectButtonWithText(latest, size: 3f, display: MilestoneDisplay.None); prevLatestMilestone = latest; } } @@ -343,18 +345,18 @@ public override void Build(ImGui gui) using (gui.EnterGroup(new Padding(0.5f), RectAllocator.LeftRow)) { gui.spacing = 0.2f; - gui.BuildFactorioObjectIcon(current, size:3f); + gui.BuildFactorioObjectIcon(current, size: 3f); gui.BuildText(current.locName, Font.subheader); gui.allocator = RectAllocator.RightAlign; gui.BuildText(CostAnalysis.GetDisplayCost(current)); var amount = CostAnalysis.Instance.GetItemAmount(current); if (amount != null) - gui.BuildText(amount, wrap:true); + gui.BuildText(amount, wrap: true); } if (gui.BuildFactorioObjectButton(gui.lastRect, current, SchemeColor.Grey)) SelectObjectPanel.Select(Database.goods.all, "Select item", SetItem); - + using (var split = gui.EnterHorizontalSplit(2)) { split.Next(); @@ -369,13 +371,13 @@ public override void Build(ImGui gui) { if (gui.BuildLink("What do colored bars mean?")) { - MessageBox.Show("How to read colored bars", + MessageBox.Show("How to read colored bars", "Blue bar means estimated production or consumption of the thing you selected. Blue bar at 50% means that that recipe produces(consumes) 50% of the product.\n\n" + "Orange bar means estimated recipe efficiency. If it is not full, the recipe looks inefficient to YAFC.\n\n" + "It is possible for a recipe to be efficient but not useful - for example a recipe that produces something that is not useful.\n\n" + "YAFC only estimates things that are required for science recipes. So buildings, belts, weapons, fuel - are not shown in estimations.", "Ok"); } - if (gui.BuildCheckBox("Current milestones info", atCurrentMilestones, out atCurrentMilestones, allocator:RectAllocator.RightRow)) + if (gui.BuildCheckBox("Current milestones info", atCurrentMilestones, out atCurrentMilestones, allocator: RectAllocator.RightRow)) { var item = current; current = null; diff --git a/YAFC/Windows/ProjectPageSettingsPanel.cs b/YAFC/Windows/ProjectPageSettingsPanel.cs index facd0bd9..1cd06498 100644 --- a/YAFC/Windows/ProjectPageSettingsPanel.cs +++ b/YAFC/Windows/ProjectPageSettingsPanel.cs @@ -21,7 +21,7 @@ public class ProjectPageSettingsPanel : PseudoScreen private string name; private FactorioObject icon; private Action callback; - + public static void Build(ImGui gui, ref string name, FactorioObject icon, Action setIcon) { gui.BuildTextInput(name, out name, "Input name"); @@ -42,7 +42,7 @@ public static void Show(ProjectPage page, Action callbac Instance.callback = callback; MainScreen.Instance.ShowPseudoScreen(Instance); } - + public override void Build(ImGui gui) { gui.spacing = 3f; @@ -55,13 +55,13 @@ public override void Build(ImGui gui) using (gui.EnterRow(0.5f, RectAllocator.RightRow)) { - if (editingPage == null && gui.BuildButton("Create", active:!string.IsNullOrEmpty(name))) + if (editingPage == null && gui.BuildButton("Create", active: !string.IsNullOrEmpty(name))) { callback?.Invoke(name, icon); Close(); } - if (editingPage != null && gui.BuildButton("OK", active:!string.IsNullOrEmpty(name))) + if (editingPage != null && gui.BuildButton("OK", active: !string.IsNullOrEmpty(name))) { if (editingPage.name != name || editingPage.icon != icon) { @@ -74,7 +74,7 @@ public override void Build(ImGui gui) if (gui.BuildButton("Cancel", SchemeColor.Grey)) Close(); - if (editingPage != null && gui.BuildButton("Other tools", SchemeColor.Grey, active:!string.IsNullOrEmpty(name))) + if (editingPage != null && gui.BuildButton("Other tools", SchemeColor.Grey, active: !string.IsNullOrEmpty(name))) { gui.ShowDropDown(OtherToolsDropdown); } @@ -90,7 +90,7 @@ public override void Build(ImGui gui) private void OtherToolsDropdown(ImGui gui) { - if (gui.BuildContextMenuButton("Duplicate page")) + if (editingPage.guid != MainScreen.SummaryGuid && gui.BuildContextMenuButton("Duplicate page")) { gui.CloseDropdown(); var project = editingPage.owner; @@ -109,7 +109,7 @@ private void OtherToolsDropdown(ImGui gui) } } - if (gui.BuildContextMenuButton("Share (export string to clipboard)")) + if (editingPage.guid != MainScreen.SummaryGuid && gui.BuildContextMenuButton("Share (export string to clipboard)")) { gui.CloseDropdown(); var data = JsonUtils.SaveToJson(editingPage); @@ -244,7 +244,7 @@ public static void LoadProjectPageFromClipboard() DataUtils.ReadLine(bytes, ref index); // reserved 1 if (DataUtils.ReadLine(bytes, ref index) != "") // reserved 2 but this time it is requried to be empty throw new NotSupportedException("Share string was created with future version of YAFC (" + version + ") and is incompatible"); - page = JsonUtils.LoadFromJson(new ReadOnlySpan(bytes, index, (int) ms.Length - index), project, collector); + page = JsonUtils.LoadFromJson(new ReadOnlySpan(bytes, index, (int)ms.Length - index), project, collector); } } } @@ -255,7 +255,7 @@ public static void LoadProjectPageFromClipboard() if (page != null) { - var existing = project.FindPage(page.guid); + var existing = project.FindPage(page.guid); if (existing != null) { MessageBox.Show((haveChoice, choice) => diff --git a/YAFC/Windows/WelcomeScreen.cs b/YAFC/Windows/WelcomeScreen.cs index c0cbfb8d..028b2b97 100644 --- a/YAFC/Windows/WelcomeScreen.cs +++ b/YAFC/Windows/WelcomeScreen.cs @@ -18,9 +18,9 @@ public class WelcomeScreen : WindowUtility, IProgress<(string, string)> private bool expensive; private string createText; private bool canCreate; - private readonly VerticalScrollCustom errorScroll; - private readonly VerticalScrollCustom recentProjectScroll; - private readonly VerticalScrollCustom languageScroll; + private readonly ScrollArea errorScroll; + private readonly ScrollArea recentProjectScroll; + private readonly ScrollArea languageScroll; private string errorMod; private string errorMessage; private string tip; @@ -49,7 +49,7 @@ public class WelcomeScreen : WindowUtility, IProgress<(string, string)> {"tr", "Turkish"}, {"uk", "Ukrainian"}, }; - + private static readonly Dictionary languagesRequireFontOverride = new Dictionary() { {"ja", "Japanese"}, @@ -69,10 +69,10 @@ public WelcomeScreen() : base(ImGuiUtils.DefaultScreenPadding) RenderingUtils.SetColorScheme(Preferences.Instance.darkMode); var lastProject = Preferences.Instance.recentProjects.FirstOrDefault(); SetProject(lastProject); - errorScroll = new VerticalScrollCustom(20f, BuildError, collapsible:true); - recentProjectScroll = new VerticalScrollCustom(20f, BuildRecentProjectList, collapsible:true); - languageScroll = new VerticalScrollCustom(20f, LanguageSelection, collapsible: true); - Create("Welcome to YAFC v"+YafcLib.version.ToString(3), 45, null); + errorScroll = new ScrollArea(20f, BuildError, collapsible: true); + recentProjectScroll = new ScrollArea(20f, BuildRecentProjectList, collapsible: true); + languageScroll = new ScrollArea(20f, LanguageSelection, collapsible: true); + Create("Welcome to YAFC v" + YafcLib.version.ToString(3), 45, null); IconCollection.ClearCustomIcons(); if (tips == null) tips = File.ReadAllLines("Data/Tips.txt"); @@ -81,22 +81,22 @@ public WelcomeScreen() : base(ImGuiUtils.DefaultScreenPadding) private void BuildError(ImGui gui) { if (errorMod != null) - gui.BuildText("Error While loading mod "+errorMod, Font.text, align:RectAlignment.Middle, color:SchemeColor.Error); + gui.BuildText("Error While loading mod " + errorMod, Font.text, align: RectAlignment.Middle, color: SchemeColor.Error); gui.allocator = RectAllocator.Stretch; - gui.BuildText(errorMessage, Font.text, color:SchemeColor.ErrorText, wrap:true); + gui.BuildText(errorMessage, Font.text, color: SchemeColor.ErrorText, wrap: true); gui.DrawRectangle(gui.lastRect, SchemeColor.Error); } protected override void BuildContents(ImGui gui) { gui.spacing = 1.5f; - gui.BuildText("Yet Another Factorio Calculator", Font.header, align:RectAlignment.Middle); + gui.BuildText("Yet Another Factorio Calculator", Font.header, align: RectAlignment.Middle); if (loading) { - gui.BuildText(currentLoad1, align:RectAlignment.Middle); - gui.BuildText(currentLoad2, align:RectAlignment.Middle); + gui.BuildText(currentLoad1, align: RectAlignment.Middle); + gui.BuildText(currentLoad2, align: RectAlignment.Middle); gui.AllocateSpacing(15f); - gui.BuildText(tip, wrap:true, align:RectAlignment.Middle); + gui.BuildText(tip, wrap: true, align: RectAlignment.Middle); gui.SetNextRebuild(Ui.time + 30); } else if (errorMessage != null) @@ -113,8 +113,8 @@ protected override void BuildContents(ImGui gui) errorMessage = null; Rebuild(); } - } - else + } + else { BuildPathSelect(gui, ref path, "Project file location", "You can leave it empty for a new project", EditType.Workspace); BuildPathSelect(gui, ref dataPath, "Factorio Data location*\nIt should contain folders 'base' and 'core'", @@ -133,7 +133,7 @@ protected override void BuildContents(ImGui gui) gui.ShowDropDown(x => languageScroll.Build(x)); gui.BuildText("In-game objects language:"); } - + using (gui.EnterRow()) { if (Preferences.Instance.recentProjects.Length > 1) @@ -149,7 +149,7 @@ protected override void BuildContents(ImGui gui) RenderingUtils.SetColorScheme(Preferences.Instance.darkMode); Preferences.Instance.Save(); } - if (gui.RemainingRow().BuildButton(createText, active:canCreate)) + if (gui.RemainingRow().BuildButton(createText, active: canCreate)) LoadProject(); } } @@ -158,13 +158,13 @@ protected override void BuildContents(ImGui gui) private void ProjectErrorMoreInfo(ImGui gui) { gui.allocator = RectAllocator.LeftAlign; - gui.BuildText("Check that these mods load in Factorio", wrap:true); - gui.BuildText("YAFC only supports loading mods that were loaded in Factorio before. If you add or remove mods or change startup settings, you need to load those in Factorio and then close the game because Factorio writes some files only when exiting", wrap:true); - gui.BuildText("Check that Factorio loads mods from the same folder as YAFC", wrap:true); - gui.BuildText("If that doesn't help, try removing all the mods that are present but aren't loaded because they are disabled, don't have required dependencies, or (especially) have several versions", wrap:true); + gui.BuildText("Check that these mods load in Factorio", wrap: true); + gui.BuildText("YAFC only supports loading mods that were loaded in Factorio before. If you add or remove mods or change startup settings, you need to load those in Factorio and then close the game because Factorio writes some files only when exiting", wrap: true); + gui.BuildText("Check that Factorio loads mods from the same folder as YAFC", wrap: true); + gui.BuildText("If that doesn't help, try removing all the mods that are present but aren't loaded because they are disabled, don't have required dependencies, or (especially) have several versions", wrap: true); if (gui.BuildLink("If that doesn't help either, create a github issue")) Ui.VisitLink(AboutScreen.Github); - gui.BuildText("For these types of errors simple mod list will not be enough. You need to attach a 'New game' savegame for syncing mods, mod versions and mod settings.", wrap:true); + gui.BuildText("For these types of errors simple mod list will not be enough. You need to attach a 'New game' savegame for syncing mods, mod versions and mod settings.", wrap: true); } private void DoLanguageList(ImGui gui, Dictionary list, bool enabled) @@ -186,18 +186,18 @@ private void LanguageSelection(ImGui gui) { gui.spacing = 0f; gui.allocator = RectAllocator.LeftAlign; - gui.BuildText("Mods may not support your language, using English as a fallback.", wrap:true); + gui.BuildText("Mods may not support your language, using English as a fallback.", wrap: true); gui.AllocateSpacing(0.5f); - + DoLanguageList(gui, languageMapping, true); if (!Program.hasOverriddenFont) { gui.AllocateSpacing(0.5f); - gui.BuildText("To select languages with non-european glyphs you need to override used font first. Download or locate a font that has your language glyphs.", wrap:true); + gui.BuildText("To select languages with non-european glyphs you need to override used font first. Download or locate a font that has your language glyphs.", wrap: true); gui.AllocateSpacing(0.5f); } DoLanguageList(gui, languagesRequireFontOverride, Program.hasOverriddenFont); - + gui.AllocateSpacing(0.5f); if (gui.BuildButton("Select font to override")) SelectFont(); @@ -211,7 +211,7 @@ private void LanguageSelection(ImGui gui) Preferences.Instance.Save(); } } - gui.BuildText("Selecting font to override require YAFC restart to take effect", wrap:true); + gui.BuildText("Selecting font to override require YAFC restart to take effect", wrap: true); } private async void SelectFont() @@ -230,7 +230,7 @@ private async void SelectFont() public void Report((string, string) value) => (currentLoad1, currentLoad2) = value; private bool FactorioValid(string factorio) => !string.IsNullOrEmpty(factorio) && Directory.Exists(Path.Combine(factorio, "core")); private bool ModsValid(string mods) => string.IsNullOrEmpty(mods) || File.Exists(Path.Combine(mods, "mod-list.json")); - + private void ValidateSelection() { var factorioValid = FactorioValid(dataPath); @@ -238,7 +238,7 @@ private void ValidateSelection() var projectExists = File.Exists(path); if (projectExists) - createText = "Load '" + Path.GetFileNameWithoutExtension(path)+"'"; + createText = "Load '" + Path.GetFileNameWithoutExtension(path) + "'"; else if (path != "") { var directory = Path.GetDirectoryName(path); @@ -248,7 +248,7 @@ private void ValidateSelection() canCreate = false; return; } - createText = "Create '" + Path.GetFileNameWithoutExtension(path)+"'"; + createText = "Create '" + Path.GetFileNameWithoutExtension(path) + "'"; } else createText = "Create new project"; canCreate = factorioValid && modsValid; @@ -256,7 +256,7 @@ private void ValidateSelection() private void BuildPathSelect(ImGui gui, ref string path, string description, string placeholder, EditType editType) { - gui.BuildText(description, wrap:true); + gui.BuildText(description, wrap: true); gui.spacing = 0.5f; using (gui.EnterGroup(default, RectAllocator.RightRow)) { @@ -267,7 +267,7 @@ private void BuildPathSelect(ImGui gui, ref string path, string description, str } gui.spacing = 1.5f; } - + private void SetProject(RecentProject project) { expensive = project.expensive; @@ -284,7 +284,7 @@ private void SetProject(RecentProject project) rootGui.ClearFocus(); rootGui.Rebuild(); } - + private async void LoadProject() { try @@ -352,7 +352,7 @@ private async void ShowFileSelect(string description, string path, EditType type ValidateSelection(); } } - + private void BuildRecentProjectsDropdown(ImGui gui) { recentProjectScroll.Build(gui); diff --git a/YAFC/Workspace/ProductionTable/ProductionLinkSummaryScreen.cs b/YAFC/Workspace/ProductionTable/ProductionLinkSummaryScreen.cs index 33289eb6..911b502a 100644 --- a/YAFC/Workspace/ProductionTable/ProductionLinkSummaryScreen.cs +++ b/YAFC/Workspace/ProductionTable/ProductionLinkSummaryScreen.cs @@ -12,22 +12,22 @@ public class ProductionLinkSummaryScreen : PseudoScreen, IComparer<(RecipeRow ro private readonly List<(RecipeRow row, float flow)> input = new List<(RecipeRow, float)>(); private readonly List<(RecipeRow row, float flow)> output = new List<(RecipeRow, float)>(); private float totalInput, totalOutput; - private readonly VerticalScrollCustom scrollArea; + private readonly ScrollArea scrollArea; private ProductionLinkSummaryScreen() { - scrollArea = new VerticalScrollCustom(30, BuildScrollArea); + scrollArea = new ScrollArea(30, BuildScrollArea); } private void BuildScrollArea(ImGui gui) { - gui.BuildText("Production: "+DataUtils.FormatAmount(totalInput, link.goods.flowUnitOfMeasure), Font.subheader); + gui.BuildText("Production: " + DataUtils.FormatAmount(totalInput, link.goods.flowUnitOfMeasure), Font.subheader); BuildFlow(gui, input, totalInput); gui.spacing = 0.5f; - gui.BuildText("Consumption: "+DataUtils.FormatAmount(totalOutput, link.goods.flowUnitOfMeasure), Font.subheader); + gui.BuildText("Consumption: " + DataUtils.FormatAmount(totalOutput, link.goods.flowUnitOfMeasure), Font.subheader); BuildFlow(gui, output, totalOutput); if (link.flags.HasFlags(ProductionLink.Flags.LinkNotMatched) && totalInput != totalOutput) - gui.BuildText((totalInput > totalOutput ? "Overproduction: " : "Overconsumption: ") + DataUtils.FormatAmount(MathF.Abs(totalInput-totalOutput), link.goods.flowUnitOfMeasure), Font.subheader, color:SchemeColor.Error); + gui.BuildText((totalInput > totalOutput ? "Overproduction: " : "Overconsumption: ") + DataUtils.FormatAmount(MathF.Abs(totalInput - totalOutput), link.goods.flowUnitOfMeasure), Font.subheader, color: SchemeColor.Error); } public override void Build(ImGui gui) @@ -71,7 +71,7 @@ private void CalculateFlow(ProductionLink link) { input.Add((recipe, localFlow)); totalInput += localFlow; - } + } else if (localFlow < 0) { output.Add((recipe, -localFlow)); diff --git a/YAFC/Workspace/ProductionTable/ProductionTableView.cs b/YAFC/Workspace/ProductionTable/ProductionTableView.cs index c0bba294..6c61e1a6 100644 --- a/YAFC/Workspace/ProductionTable/ProductionTableView.cs +++ b/YAFC/Workspace/ProductionTable/ProductionTableView.cs @@ -824,10 +824,10 @@ private void DrawDesiredProduct(ImGui gui, ProductionLink element) element.RecordUndo().amount = newAmount; } - public override void Rebuild(bool visuaOnly = false) + public override void Rebuild(bool visualOnly = false) { flatHierarchyBuilder.SetData(model); - base.Rebuild(visuaOnly); + base.Rebuild(visualOnly); } private void BuildGoodsIcon(ImGui gui, Goods goods, ProductionLink link, float amount, ProductDropdownType dropdownType, RecipeRow recipe, ProductionTable context, Goods[] variants = null) diff --git a/YAFC/Workspace/SummaryView.cs b/YAFC/Workspace/SummaryView.cs new file mode 100644 index 00000000..5c4c46fc --- /dev/null +++ b/YAFC/Workspace/SummaryView.cs @@ -0,0 +1,315 @@ +using System; +using System.Collections.Generic; +using YAFC.Model; +using YAFC.UI; + +namespace YAFC +{ + public class SummaryView : ProjectPageView + { + private class SummaryScrollArea : ScrollArea + { + static float DefaultHeight = 10; + + public SummaryScrollArea(GuiBuilder builder) : base(DefaultHeight, builder, default, false, true, true) + { + } + + public new void Build(ImGui gui) + { + // Maximize scroll area to fit parent area (minus header and 'show issues' heights, and some (2) padding probably) + Build(gui, gui.valid ? gui.parent.contentSize.Y - HeaderFont.size - Font.text.size - ScrollbarSize - 2 : DefaultHeight); + } + } + + private class SummaryTabColumn : TextDataColumn + { + public SummaryTabColumn() : base("Tab", 6f) + { + } + + public override void BuildElement(ImGui gui, ProjectPage page) + { + if (page?.contentType != typeof(ProductionTable)) + { + return; + } + + using (gui.EnterGroup(new Padding(0.5f, 0.2f, 0.2f, 0.5f))) + { + gui.spacing = 0.2f; + if (page.icon != null) + gui.BuildIcon(page.icon.icon); + else gui.AllocateRect(0f, 1.5f); + gui.BuildText(page.name); + } + } + } + + private class SummaryDataColumn : TextDataColumn + { + protected readonly SummaryView view; + + public SummaryDataColumn(SummaryView view) : base("Linked", float.MaxValue) + { + this.view = view; + } + + public override void BuildElement(ImGui gui, ProjectPage page) + { + if (page?.contentType != typeof(ProductionTable)) + { + return; + } + + var table = page.content as ProductionTable; + using var grid = gui.EnterInlineGrid(ElementWidth, ElementSpacing); + foreach (KeyValuePair goodInfo in view.allGoods) + { + if (!view.searchQuery.Match(goodInfo.Key)) + { + continue; + } + + float amountAvailable = YAFCRounding((goodInfo.Value.totalProvided > 0 ? goodInfo.Value.totalProvided : 0) + goodInfo.Value.extraProduced); + float amountNeeded = YAFCRounding((goodInfo.Value.totalProvided < 0 ? -goodInfo.Value.totalProvided : 0) + goodInfo.Value.totalNeeded); + if (view.model.showOnlyIssues && (Math.Abs(amountAvailable - amountNeeded) < Epsilon || amountNeeded == 0)) + { + continue; + } + + grid.Next(); + bool enoughProduced = amountAvailable >= amountNeeded; + ProductionLink link = table.links.Find(x => x.goods.name == goodInfo.Key); + if (link != null) + { + if (link.amount != 0f) + { + DrawProvideProduct(gui, link, page, goodInfo.Value, enoughProduced); + } + } + else + { + if (Array.Exists(table.flow, x => x.goods.name == goodInfo.Key)) + { + ProductionTableFlow flow = Array.Find(table.flow, x => x.goods.name == goodInfo.Key); + if (Math.Abs(flow.amount) > Epsilon) + { + + DrawRequestProduct(gui, flow, enoughProduced); + } + } + } + } + } + + static private void DrawProvideProduct(ImGui gui, ProductionLink element, ProjectPage page, GoodDetails goodInfo, bool enoughProduced) + { + gui.allocator = RectAllocator.Stretch; + gui.spacing = 0f; + + GoodsWithAmountEvent evt = gui.BuildFactorioObjectWithEditableAmount(element.goods, element.amount, element.goods.flowUnitOfMeasure, out float newAmount, (element.amount > 0 && enoughProduced) || (element.amount < 0 && goodInfo.extraProduced == -element.amount) ? SchemeColor.Primary : SchemeColor.Error); + if (evt == GoodsWithAmountEvent.TextEditing && newAmount != 0) + { + SetProviderAmount(element, page, newAmount); + } + else if (evt == GoodsWithAmountEvent.ButtonClick) + { + SetProviderAmount(element, page, YAFCRounding(goodInfo.sum)); + } + } + static private void DrawRequestProduct(ImGui gui, ProductionTableFlow flow, bool enoughProduced) + { + gui.allocator = RectAllocator.Stretch; + gui.spacing = 0f; + gui.BuildFactorioObjectWithAmount(flow.goods, -flow.amount, flow.goods?.flowUnitOfMeasure ?? UnitOfMeasure.None, flow.amount > Epsilon ? enoughProduced ? SchemeColor.Green : SchemeColor.Error : SchemeColor.None); + } + + static private void SetProviderAmount(ProductionLink element, ProjectPage page, float newAmount) + { + element.RecordUndo().amount = newAmount; + // Hack: Force recalculate the page (and make sure to catch the content change event caused by the recalculation) + page.SetActive(true); + page.SetToRecalculate(); + page.SetActive(false); + } + } + + static readonly float Epsilon = 1e-5f; + static readonly float ElementWidth = 3; + static readonly float ElementSpacing = 1; + struct GoodDetails + { + public float totalProvided; + public float totalNeeded; + public float extraProduced; + public float sum; + } + + static Font HeaderFont = Font.header; + + private Project project; + private SearchQuery searchQuery; + + private readonly SummaryScrollArea scrollArea; + private readonly SummaryDataColumn goodsColumn; + private readonly DataGrid mainGrid; + + private readonly Dictionary allGoods = new Dictionary(); + + + public SummaryView() + { + goodsColumn = new SummaryDataColumn(this); + var columns = new TextDataColumn[] + { + new SummaryTabColumn(), + goodsColumn, + }; + scrollArea = new SummaryScrollArea(BuildScrollArea); + mainGrid = new DataGrid(columns); + } + + public void SetProject(Project project) + { + if (this.project != null) + { + this.project.metaInfoChanged -= Recalculate; + foreach (ProjectPage page in project.pages) + { + page.contentChanged -= Recalculate; + } + } + + this.project = project; + + project.metaInfoChanged += Recalculate; + foreach (ProjectPage page in project.pages) + { + page.contentChanged += Recalculate; + } + } + + protected override void BuildPageTooltip(ImGui gui, Summary contents) + { + } + + protected override void BuildHeader(ImGui gui) + { + base.BuildHeader(gui); + + gui.allocator = RectAllocator.Center; + gui.BuildText("Production Sheet Summary", HeaderFont, false, RectAlignment.Middle); + gui.allocator = RectAllocator.LeftAlign; + } + + protected override void BuildContent(ImGui gui) + { + if (gui.BuildCheckBox("Only show issues", model.showOnlyIssues, out bool newValue)) + { + model.showOnlyIssues = newValue; + Recalculate(); + } + + scrollArea.Build(gui); + } + + private void BuildScrollArea(ImGui gui) + { + foreach (Guid displayPage in project.displayPages) + { + ProjectPage page = project.FindPage(displayPage); + if (page?.contentType != typeof(ProductionTable)) + continue; + + mainGrid.BuildRow(gui, page); + } + } + + private void Recalculate() => Recalculate(false); + + private void Recalculate(bool visualOnly) + { + allGoods.Clear(); + foreach (Guid displayPage in project.displayPages) + { + ProjectPage page = project.FindPage(displayPage); + ProductionTable content = page?.content as ProductionTable; + if (content == null) + { + continue; + } + + foreach (ProductionLink link in content.links) + { + if (link.amount != 0f) + { + GoodDetails value = allGoods.GetValueOrDefault(link.goods.name); + value.totalProvided += YAFCRounding(link.amount); ; + allGoods[link.goods.name] = value; + } + } + + foreach (ProductionTableFlow flow in content.flow) + { + if (flow.amount < -Epsilon) + { + GoodDetails value = allGoods.GetValueOrDefault(flow.goods.name); + value.totalNeeded -= YAFCRounding(flow.amount); ; + value.sum -= YAFCRounding(flow.amount); ; + allGoods[flow.goods.name] = value; + } + else if (flow.amount > Epsilon) + { + if (!content.links.Exists(x => x.goods == flow.goods)) + { + // Only count extras if not linked + GoodDetails value = allGoods.GetValueOrDefault(flow.goods.name); + value.extraProduced += YAFCRounding(flow.amount); + value.sum -= YAFCRounding(flow.amount); + allGoods[flow.goods.name] = value; + } + } + } + } + + int count = 0; + foreach (KeyValuePair entry in allGoods) + { + float amountAvailable = YAFCRounding((entry.Value.totalProvided > 0 ? entry.Value.totalProvided : 0) + entry.Value.extraProduced); + float amountNeeded = YAFCRounding((entry.Value.totalProvided < 0 ? -entry.Value.totalProvided : 0) + entry.Value.totalNeeded); + if (model != null && model.showOnlyIssues && (Math.Abs(amountAvailable - amountNeeded) < Epsilon || amountNeeded == 0)) + { + continue; + } + count++; + } + + goodsColumn.width = count * (ElementWidth + ElementSpacing); + + Rebuild(visualOnly); + scrollArea.RebuildContents(); + } + + // Convert/truncate value as shown in UI to prevent slight mismatches + static private float YAFCRounding(float value) + { +#pragma warning disable CA1806 // We don't care about the returned value as result is updated independently whether the function return true or not + DataUtils.TryParseAmount(DataUtils.FormatAmount(value, UnitOfMeasure.Second), out float result, UnitOfMeasure.Second); +#pragma warning restore CA1806 + + return result; + } + + public override void SetSearchQuery(SearchQuery query) + { + searchQuery = query; + bodyContent.Rebuild(); + scrollArea.Rebuild(); + } + + public override void CreateModelDropdown(ImGui gui, Type type, Project project) + { + } + } +} \ No newline at end of file diff --git a/YAFCmodel/Data/DataUtils.cs b/YAFCmodel/Data/DataUtils.cs index 016b3de0..4f16aada 100644 --- a/YAFCmodel/Data/DataUtils.cs +++ b/YAFCmodel/Data/DataUtils.cs @@ -427,11 +427,11 @@ public static string FormatTime(float time) public static string FormatAmount(float amount, UnitOfMeasure unit, string prefix = null, string suffix = null, bool precise = false) { - var (multplier, unitSuffix) = Project.current == null ? (1f, null) : Project.current.ResolveUnitOfMeasure(unit); - return FormatAmountRaw(amount, multplier, unitSuffix, prefix, suffix, precise ? PreciseFormat : FormatSpec); + var (multiplier, unitSuffix) = Project.current == null ? (1f, null) : Project.current.ResolveUnitOfMeasure(unit); + return FormatAmountRaw(amount, multiplier, unitSuffix, prefix, suffix, precise ? PreciseFormat : FormatSpec); } - public static string FormatAmountRaw(float amount, float unitMultipler, string unitSuffix, string prefix = null, string suffix = null, (char suffix, float multiplier, string format)[] formatSpec = null) + public static string FormatAmountRaw(float amount, float unitMultiplier, string unitSuffix, string prefix = null, string suffix = null, (char suffix, float multiplier, string format)[] formatSpec = null) { if (float.IsNaN(amount) || float.IsInfinity(amount)) return "-"; @@ -447,7 +447,7 @@ public static string FormatAmountRaw(float amount, float unitMultipler, string u amount = -amount; } - amount *= unitMultipler; + amount *= unitMultiplier; var idx = MathUtils.Clamp(MathUtils.Floor(MathF.Log10(amount)) + 8, 0, formatSpec.Length-1); var val = formatSpec[idx]; amountBuilder.Append((amount * val.multiplier).ToString(val.format)); diff --git a/YAFCmodel/Model/Summary.cs b/YAFCmodel/Model/Summary.cs new file mode 100644 index 00000000..1a936a48 --- /dev/null +++ b/YAFCmodel/Model/Summary.cs @@ -0,0 +1,17 @@ +using System.Threading.Tasks; + +namespace YAFC.Model +{ + public class Summary : ProjectPageContents + { + + public bool showOnlyIssues { get; set; } + + public Summary(ModelObject page) : base(page) { } + + public override async Task Solve(ProjectPage page) + { + return null; + } + } +} \ No newline at end of file diff --git a/YAFCui/ImGui/ImGui.cs b/YAFCui/ImGui/ImGui.cs index ae05e4dc..83575816 100644 --- a/YAFCui/ImGui/ImGui.cs +++ b/YAFCui/ImGui/ImGui.cs @@ -47,26 +47,26 @@ public enum RectAllocator Center, LeftRow, RightRow, - RemainigRow, + RemainingRow, FixedRect, HalfRow } public delegate void GuiBuilder(ImGui gui); - + public sealed partial class ImGui : IDisposable, IPanel { - public ImGui(GuiBuilder gui, Padding padding, RectAllocator defaultAllocator = RectAllocator.Stretch, bool clip = false) + public ImGui(GuiBuilder guiBuilder, Padding padding, RectAllocator defaultAllocator = RectAllocator.Stretch, bool clip = false) { - this.gui = gui; - if (gui == null) + this.guiBuilder = guiBuilder; + if (guiBuilder == null) action = ImGuiAction.Build; this.defaultAllocator = defaultAllocator; this.clip = clip; initialPadding = padding; } - - public readonly GuiBuilder gui; + + public readonly GuiBuilder guiBuilder; public Window window { get; private set; } public ImGui parent { get; private set; } IPanel IPanel.Parent => parent; @@ -77,7 +77,7 @@ public ImGui(GuiBuilder gui, Padding padding, RectAllocator defaultAllocator = R private bool disposed; public Vector2 contentSize { get; private set; } public ImGuiAction action { get; private set; } - + public bool isBuilding { [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -92,7 +92,7 @@ public bool isBuilding private Vector2 _offset; private Rect screenRect; private Rect localClip; - + public Vector2 offset { get => _offset; @@ -132,12 +132,12 @@ public void SetNextRebuild(long nextRebuildTime) window.SetNextRepaint(nextRebuildTime); } } - + public void Repaint() { window?.Repaint(); } - + public Vector2 CalculateState(float width, float pixelsPerUnit) { if (IsRebuildRequired() || buildWidth != width || this.pixelsPerUnit != pixelsPerUnit) @@ -147,7 +147,7 @@ public Vector2 CalculateState(float width, float pixelsPerUnit) } return contentSize; } - + public void Present(DrawingSurface surface, Rect position, Rect screenClip, ImGui parent) { if (parent != null) @@ -170,7 +170,7 @@ internal void InternalPresent(DrawingSurface surface, Rect position, Rect screen if (clip) prevClip = surface.SetClip(ToSdlRect(screenClip)); localClip = new Rect(screenClip.Position - screenOffset, screenClip.Size / scale); - var currentColor = (SchemeColor) (-1); + var currentColor = (SchemeColor)(-1); borders.Clear(); for (var i = rects.Count - 1; i >= 0; i--) { @@ -190,7 +190,7 @@ internal void InternalPresent(DrawingSurface surface, Rect position, Rect screen } SDL.SDL_RenderFillRect(renderer, ref sdlRect); } - + foreach (var (pos, icon, color) in icons) { if (!pos.IntersectsWith(localClip)) @@ -220,7 +220,7 @@ internal void InternalPresent(DrawingSurface surface, Rect position, Rect screen if (clip) surface.SetClip(prevClip); } - + public IPanel HitTest(Vector2 position) { position = position / scale - offset; @@ -234,7 +234,7 @@ public IPanel HitTest(Vector2 position) return this; } - public int UnitsToPixels(float units) => (int) MathF.Round(units * pixelsPerUnit); + public int UnitsToPixels(float units) => (int)MathF.Round(units * pixelsPerUnit); public float PixelsToUnits(int pixels) => pixels / pixelsPerUnit; public SDL.SDL_Rect ToSdlRect(Rect rect, Vector2 offset = default) { @@ -246,7 +246,7 @@ public SDL.SDL_Rect ToSdlRect(Rect rect, Vector2 offset = default) h = UnitsToPixels(rect.Height) }; } - + private static void CheckMainThread() { if (!Ui.IsMainThread()) @@ -272,13 +272,13 @@ public Vector2 FromWindowPosition(Vector2 windowPosition) return windowPosition; return (windowPosition - screenRect.Position) * (pixelsPerUnit / window.pixelsPerUnit); } - + private void ReleaseUnmanagedResources() { disposed = true; textCache.Dispose(); } - + public void Dispose() { ReleaseUnmanagedResources(); @@ -302,7 +302,7 @@ private void ExportDrawCommandsTo(List> sourceList, List> rects = new List>(); private readonly List> icons = new List>(); private readonly List> renderables = new List>(); @@ -75,10 +75,10 @@ private void ClearDrawCommandList() public void ManualDrawingClear() { - if (gui == null) + if (guiBuilder == null) ClearDrawCommandList(); } - + public readonly ImGuiCache.Cache textCache = new ImGuiCache.Cache(); public FontFile.FontSize GetFontSize(Font font = null) => (font ?? Font.text).GetFontSize(pixelsPerUnit); @@ -108,7 +108,7 @@ public Rect AllocateTextRect(out TextCache cache, string text, Font font = null, } else { - cache = textCache.GetCached((fontSize, text, wrap ? (uint) UnitsToPixels(MathF.Max(width, 5f)) : uint.MaxValue)); + cache = textCache.GetCached((fontSize, text, wrap ? (uint)UnitsToPixels(MathF.Max(width, 5f)) : uint.MaxValue)); rect = AllocateRect(cache.texRect.w / pixelsPerUnit, topOffset + cache.texRect.h / pixelsPerUnit, align); } @@ -141,7 +141,7 @@ public bool BuildTextInput(string text, out string newText, string placeholder, textInputHelper = new ImGuiTextInputHelper(this); return textInputHelper.BuildTextInput(text, out newText, placeholder, GetFontSize(), delayed, icon, padding, alignment, color); } - + public void BuildIcon(Icon icon, float size = 1.5f, SchemeColor color = SchemeColor.None) { if (color == SchemeColor.None) @@ -162,7 +162,7 @@ public void BuildIcon(Icon icon, float size = 1.5f, SchemeColor color = SchemeCo private bool DoGui(ImGuiAction action) { - if (gui == null) + if (guiBuilder == null) return false; this.action = action; ResetLayout(); @@ -170,7 +170,7 @@ private bool DoGui(ImGuiAction action) buildGroupsIndex = -1; using (EnterGroup(initialPadding, defaultAllocator, initialTextColor)) { - gui(this); + guiBuilder(this); } actionParameter = 0; if (action == ImGuiAction.Build) @@ -184,14 +184,14 @@ private bool DoGui(ImGuiAction action) private void BuildGui(float width) { - if (gui == null) + if (guiBuilder == null) return; buildWidth = width; nextRebuildTimer = long.MaxValue; rebuildRequested = false; ClearDrawCommandList(); DoGui(ImGuiAction.Build); - contentSize = new Vector2(buildingWidth, lastRect.Bottom); + contentSize = new Vector2(lastContentRect.Width, lastContentRect.Height); if (boxColor != SchemeColor.None) { var rect = new Rect(default, contentSize); @@ -252,7 +252,7 @@ public void MouseUp(int button) SDL.SDL_SetCursor(RenderingUtils.cursorHand); cursorSetByMouseDown = false; } - + actionParameter = button; DoGui(ImGuiAction.MouseUp); } @@ -354,7 +354,7 @@ public void SetFocus(Rect rect) mouseDownRect = rect; Rebuild(); } - + public void SetTextInputFocus(Rect rect, string text) { if (textInputHelper != null && InputSystem.Instance.currentKeyboardFocus != textInputHelper) @@ -366,7 +366,6 @@ public void SetTextInputFocus(Rect rect, string text) public void SaveToImage() { - } } } \ No newline at end of file diff --git a/YAFCui/ImGui/ImGuiLayout.cs b/YAFCui/ImGui/ImGuiLayout.cs index 7e21a329..30b1e163 100644 --- a/YAFCui/ImGui/ImGuiLayout.cs +++ b/YAFCui/ImGui/ImGuiLayout.cs @@ -8,11 +8,12 @@ public partial class ImGui { private CopyableState state; public Rect lastRect { get; set; } + public Rect lastContentRect { get; set; } public float width => state.right - state.left; public Rect statePosition => new Rect(state.left, state.top, width, 0f); public ref RectAllocator allocator => ref state.allocator; public ref float spacing => ref state.spacing; - public Rect layoutRect => new Rect(state.left, state.top, state.bottom - state.top, state.right - state.left); + public Rect layoutRect => new Rect(state.left, state.top, state.right - state.left, state.bottom - state.top); private void ResetLayout() { @@ -38,7 +39,8 @@ public Rect AllocateRect(float width, float height, float spacing = float.Negati public Rect EncapsulateRect(Rect rect) { - return lastRect = state.EncapsulateRect(rect); + lastRect = state.EncapsulateRect(rect); + return lastRect; } public Rect AllocateRect(float width, float height, RectAlignment alignment, float spacing = float.NegativeInfinity) @@ -54,15 +56,15 @@ public static Rect AlignRect(Rect boundary, RectAlignment alignment, float width switch (alignment) { case RectAlignment.Middle: - return new Rect(boundary.X + (boundary.Width - width) * 0.5f, boundary.Y + (boundary.Height-height) * 0.5f, width, height); + return new Rect(boundary.X + (boundary.Width - width) * 0.5f, boundary.Y + (boundary.Height - height) * 0.5f, width, height); case RectAlignment.MiddleLeft: - return new Rect(boundary.X, boundary.Y + (boundary.Height-height) * 0.5f, width, height); + return new Rect(boundary.X, boundary.Y + (boundary.Height - height) * 0.5f, width, height); case RectAlignment.MiddleRight: - return new Rect(boundary.X, boundary.Y + (boundary.Height-height) * 0.5f, width, height); + return new Rect(boundary.X, boundary.Y + (boundary.Height - height) * 0.5f, width, height); case RectAlignment.UpperCenter: return new Rect(boundary.X + (boundary.Width - width) * 0.5f, boundary.Y, width, height); case RectAlignment.MiddleFullRow: - return new Rect(boundary.X, boundary.Y + (boundary.Height-height) * 0.5f, boundary.Width, height); + return new Rect(boundary.X, boundary.Y + (boundary.Height - height) * 0.5f, boundary.Width, height); default: return boundary; } @@ -71,7 +73,7 @@ public static Rect AlignRect(Rect boundary, RectAlignment alignment, float width public ImGui RemainingRow(float spacing = float.NegativeInfinity) { state.AllocateSpacing(spacing); - allocator = RectAllocator.RemainigRow; + allocator = RectAllocator.RemainingRow; return this; } @@ -102,7 +104,7 @@ public Context EnterFixedPositioning(float width, float height, Padding padding, state.textColor = textColor; return context; } - + private struct CopyableState { public RectAllocator allocator; @@ -121,23 +123,23 @@ public Rect AllocateRect(float width, float height, float spacing) switch (allocator) { case RectAllocator.Stretch: - return new Rect(left, top, right-left, height); + return new Rect(left, top, right - left, height); case RectAllocator.LeftAlign: return new Rect(left, top, width, height); case RectAllocator.RightAlign: - return new Rect(right-width, top, width, height); + return new Rect(right - width, top, width, height); case RectAllocator.Center: - return new Rect((right+left-width) * 0.5f, top, width, height); + return new Rect((right + left - width) * 0.5f, top, width, height); case RectAllocator.LeftRow: return new Rect(left, top, width, rowHeight); case RectAllocator.RightRow: - return new Rect(right-width, top, width, rowHeight); - case RectAllocator.RemainigRow: - return new Rect(left, top, right-left, rowHeight); + return new Rect(right - width, top, width, rowHeight); + case RectAllocator.RemainingRow: + return new Rect(left, top, right - left, rowHeight); case RectAllocator.FixedRect: - return new Rect(left, top, right-left, rowHeight); + return new Rect(left, top, right - left, rowHeight); case RectAllocator.HalfRow: - return new Rect(left, top, (right-left-spacing)/2f, rowHeight); + return new Rect(left, top, (right - left - spacing) / 2f, rowHeight); default: throw new ArgumentOutOfRangeException(); } @@ -166,7 +168,7 @@ public void AllocateSpacing(float amount = float.NegativeInfinity) break; } } - + public Rect EncapsulateRect(Rect rect) { contextRect = hasContent ? Rect.Union(contextRect, rect) : rect; @@ -179,28 +181,28 @@ public Rect EncapsulateRect(Rect rect) rect.Width = right - left; break; case RectAllocator.RightAlign: - top = bottom = MathF.Max(rect.Bottom, top);; + top = bottom = MathF.Max(rect.Bottom, top); ; rect.Right = right; break; case RectAllocator.LeftAlign: - top = bottom = MathF.Max(rect.Bottom, top);; + top = bottom = MathF.Max(rect.Bottom, top); ; rect.Left = left; break; case RectAllocator.Center: - top = bottom = MathF.Max(rect.Bottom, top);; + top = bottom = MathF.Max(rect.Bottom, top); ; break; case RectAllocator.LeftRow: left = rect.Right; - bottom = MathF.Max(rect.Bottom, bottom);; + bottom = MathF.Max(rect.Bottom, bottom); ; break; case RectAllocator.RightRow: right = rect.Left; - bottom = MathF.Max(rect.Bottom, bottom);; + bottom = MathF.Max(rect.Bottom, bottom); ; break; case RectAllocator.HalfRow: - allocator = RectAllocator.RemainigRow; + allocator = RectAllocator.RemainingRow; left = rect.Right + spacing; - bottom = MathF.Max(rect.Bottom, bottom);; + bottom = MathF.Max(rect.Bottom, bottom); ; break; } @@ -240,7 +242,10 @@ public void Dispose() rect.Width += (padding.left + padding.right); rect.Height += (padding.top + padding.bottom); if (hasContent) + { gui.lastRect = gui.state.EncapsulateRect(rect); + gui.lastContentRect = rect; + } else gui.lastRect = default; } diff --git a/YAFCui/ImGui/ScrollArea.cs b/YAFCui/ImGui/ScrollArea.cs index 82ae7f91..9ed0759e 100644 --- a/YAFCui/ImGui/ScrollArea.cs +++ b/YAFCui/ImGui/ScrollArea.cs @@ -77,20 +77,20 @@ public void Build(ImGui gui, float height) { if (horizontal && maxScroll.X > 0f) { - var fullScrollRect = new Rect(rect.X, rect.Bottom-ScrollbarSize, rect.Width, ScrollbarSize); + var fullScrollRect = new Rect(rect.X, rect.Bottom - ScrollbarSize, rect.Width, ScrollbarSize); var scrollRect = new Rect(rect.X + scrollStart.X, fullScrollRect.Y, scrollSize.X, ScrollbarSize); BuildScrollBar(gui, 0, in fullScrollRect, in scrollRect); } if (vertical && maxScroll.Y > 0f) { - var fullScrollRect = new Rect(rect.Right-ScrollbarSize, rect.Y, ScrollbarSize, rect.Height); + var fullScrollRect = new Rect(rect.Right - ScrollbarSize, rect.Y, ScrollbarSize, rect.Height); var scrollRect = new Rect(fullScrollRect.X, rect.Y + scrollStart.Y, ScrollbarSize, scrollSize.Y); BuildScrollBar(gui, 1, in fullScrollRect, in scrollRect); } } } - + private void BuildScrollBar(ImGui gui, int axis, in Rect fullScrollRect, in Rect scrollRect) { switch (gui.action) @@ -132,7 +132,7 @@ public float scroll get => _scroll.Y; set => scroll2d = new Vector2(_scroll.X, value); } - + public float scrollX { get => _scroll.X; @@ -175,17 +175,17 @@ public bool KeyDown(SDL.SDL_Keysym key) public bool TextInput(string input) => false; public bool KeyUp(SDL.SDL_Keysym key) => false; - public void FocusChanged(bool focused) {} + public void FocusChanged(bool focused) { } } - - public abstract class ScrollArea : Scrollable + + public abstract class ScrollAreaBase : Scrollable { - protected readonly ImGui contents; + protected ImGui contents; protected readonly float height; - public ScrollArea(float height, Padding padding, bool collapsible = false, bool vertical = true, bool horizontal = false) : base(vertical, horizontal, collapsible) + public ScrollAreaBase(float height, Padding padding, bool collapsible = false, bool vertical = true, bool horizontal = false) : base(vertical, horizontal, collapsible) { - contents = new ImGui(BuildContents, padding, clip:true); + contents = new ImGui(BuildContents, padding, clip: true); this.height = height; } @@ -197,7 +197,7 @@ protected override void PositionContent(ImGui gui, Rect viewport) public void Build(ImGui gui) => Build(gui, height); protected abstract void BuildContents(ImGui gui); - + public void RebuildContents() { contents.Rebuild(); @@ -209,20 +209,20 @@ protected override Vector2 MeasureContent(Rect rect, ImGui gui) } } - public class VerticalScrollCustom : ScrollArea + public class ScrollArea : ScrollAreaBase { private readonly GuiBuilder builder; - public VerticalScrollCustom(float height, GuiBuilder builder, Padding padding = default, bool collapsible = false) : base(height, padding, collapsible) + public ScrollArea(float height, GuiBuilder builder, Padding padding = default, bool collapsible = false, bool vertical = true, bool horizontal = false) : base(height, padding, collapsible, vertical, horizontal) { this.builder = builder; } protected override void BuildContents(ImGui gui) => builder(gui); - public void Rebuild() => contents.Rebuild(); + public void Rebuild() => RebuildContents(); } - public class VirtualScrollList : ScrollArea + public class VirtualScrollList : ScrollAreaBase { private readonly Vector2 elementSize; protected readonly int bufferRows; @@ -240,7 +240,7 @@ public float spacing set { _spacing = value; - contents.Rebuild(); + RebuildContents(); } } @@ -252,7 +252,7 @@ public IReadOnlyList data set { _data = value ?? Array.Empty(); - contents.Rebuild(); + RebuildContents(); } } @@ -275,7 +275,7 @@ public override Vector2 scroll2d base.scroll2d = value; var row = CalcFirstBlock(); if (row != firstVisibleBlock) - contents.Rebuild(); + RebuildContents(); } }