diff --git a/CHANGELOG.md b/CHANGELOG.md index d24fcfba8..2e2ae070b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -34,6 +34,7 @@ All notable changes to this project will be documented in this file. - [GUI] Search by licenses (#4148 by: HebaruSan) - [CLI] Make `ckan compat add` take multiple versions, add `clear` and `set` (#4151 by: HebaruSan) - [GUI] Add mod support links to Help menu (#4154 by: HebaruSan) +- [GUI] Don't hide other providing mods for installed, make installed bold (#4163 by: HebaruSan) ### Bugfixes diff --git a/GUI/Controls/ModInfoTabs/Relationships.Designer.cs b/GUI/Controls/ModInfoTabs/Relationships.Designer.cs index d1b525457..660e5659d 100644 --- a/GUI/Controls/ModInfoTabs/Relationships.Designer.cs +++ b/GUI/Controls/ModInfoTabs/Relationships.Designer.cs @@ -32,6 +32,7 @@ private void InitializeComponent() System.ComponentModel.ComponentResourceManager resources = new SingleAssemblyComponentResourceManager(typeof(Relationships)); this.ToolTip = new System.Windows.Forms.ToolTip(); this.DependsGraphTree = new System.Windows.Forms.TreeView(); + this.LegendInstalledLabel = new System.Windows.Forms.Label(); this.LegendProvidesImage = new System.Windows.Forms.PictureBox(); this.LegendProvidesLabel = new System.Windows.Forms.Label(); this.LegendDependsImage = new System.Windows.Forms.PictureBox(); @@ -60,7 +61,7 @@ private void InitializeComponent() | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.DependsGraphTree.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.DependsGraphTree.Location = new System.Drawing.Point(3, 114); + this.DependsGraphTree.Location = new System.Drawing.Point(3, 132); this.DependsGraphTree.Name = "DependsGraphTree"; this.DependsGraphTree.Size = new System.Drawing.Size(494, 364); this.DependsGraphTree.TabIndex = 0; @@ -79,88 +80,95 @@ private void InitializeComponent() this.DependsGraphTree.ImageList.Images.Add("Supports", global::CKAN.GUI.EmbeddedImages.smile); this.DependsGraphTree.ImageList.Images.Add("Conflicts", global::CKAN.GUI.EmbeddedImages.alert); // + // LegendInstalledLabel + // + this.LegendInstalledLabel.AutoSize = true; + this.LegendInstalledLabel.Location = new System.Drawing.Point(24, 3); + this.LegendInstalledLabel.Font = new System.Drawing.Font(System.Drawing.SystemFonts.DefaultFont.Name, 8, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point); + resources.ApplyResources(this.LegendInstalledLabel, "LegendInstalledLabel"); + // // LegendProvidesImage // this.LegendProvidesImage.BackColor = System.Drawing.SystemColors.Window; this.LegendProvidesImage.Image = global::CKAN.GUI.EmbeddedImages.ballot; - this.LegendProvidesImage.Location = new System.Drawing.Point(6, 3); + this.LegendProvidesImage.Location = new System.Drawing.Point(6, 21); this.LegendProvidesImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; this.LegendProvidesImage.ClientSize = new System.Drawing.Size(14, 14); // // LegendProvidesLabel // this.LegendProvidesLabel.AutoSize = true; - this.LegendProvidesLabel.Location = new System.Drawing.Point(24, 3); + this.LegendProvidesLabel.Location = new System.Drawing.Point(24, 21); resources.ApplyResources(this.LegendProvidesLabel, "LegendProvidesLabel"); // // LegendDependsImage // this.LegendDependsImage.BackColor = System.Drawing.SystemColors.Window; this.LegendDependsImage.Image = global::CKAN.GUI.EmbeddedImages.star; - this.LegendDependsImage.Location = new System.Drawing.Point(6, 21); + this.LegendDependsImage.Location = new System.Drawing.Point(6, 39); this.LegendDependsImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; this.LegendDependsImage.ClientSize = new System.Drawing.Size(14, 14); // // LegendDependsLabel // this.LegendDependsLabel.AutoSize = true; - this.LegendDependsLabel.Location = new System.Drawing.Point(24, 21); + this.LegendDependsLabel.Location = new System.Drawing.Point(24, 39); resources.ApplyResources(this.LegendDependsLabel, "LegendDependsLabel"); // // LegendRecommendsImage // this.LegendRecommendsImage.BackColor = System.Drawing.SystemColors.Window; this.LegendRecommendsImage.Image = global::CKAN.GUI.EmbeddedImages.thumbup; - this.LegendRecommendsImage.Location = new System.Drawing.Point(6, 39); + this.LegendRecommendsImage.Location = new System.Drawing.Point(6, 57); this.LegendRecommendsImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; this.LegendRecommendsImage.ClientSize = new System.Drawing.Size(14, 14); // // LegendRecommendsLabel // this.LegendRecommendsLabel.AutoSize = true; - this.LegendRecommendsLabel.Location = new System.Drawing.Point(24, 39); + this.LegendRecommendsLabel.Location = new System.Drawing.Point(24, 57); resources.ApplyResources(this.LegendRecommendsLabel, "LegendRecommendsLabel"); // // LegendSuggestsImage // this.LegendSuggestsImage.BackColor = System.Drawing.SystemColors.Window; this.LegendSuggestsImage.Image = global::CKAN.GUI.EmbeddedImages.info; - this.LegendSuggestsImage.Location = new System.Drawing.Point(6, 57); + this.LegendSuggestsImage.Location = new System.Drawing.Point(6, 75); this.LegendSuggestsImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; this.LegendSuggestsImage.ClientSize = new System.Drawing.Size(14, 14); // // LegendSuggestsLabel // this.LegendSuggestsLabel.AutoSize = true; - this.LegendSuggestsLabel.Location = new System.Drawing.Point(24, 57); + this.LegendSuggestsLabel.Location = new System.Drawing.Point(24, 75); resources.ApplyResources(this.LegendSuggestsLabel, "LegendSuggestsLabel"); // // LegendSupportsImage // this.LegendSupportsImage.BackColor = System.Drawing.SystemColors.Window; this.LegendSupportsImage.Image = global::CKAN.GUI.EmbeddedImages.smile; - this.LegendSupportsImage.Location = new System.Drawing.Point(6, 75); + this.LegendSupportsImage.Location = new System.Drawing.Point(6, 93); this.LegendSupportsImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; this.LegendSupportsImage.ClientSize = new System.Drawing.Size(14, 14); // // LegendSupportsLabel // this.LegendSupportsLabel.AutoSize = true; - this.LegendSupportsLabel.Location = new System.Drawing.Point(24, 75); + this.LegendSupportsLabel.Location = new System.Drawing.Point(24, 93); resources.ApplyResources(this.LegendSupportsLabel, "LegendSupportsLabel"); // // LegendConflictsImage // this.LegendConflictsImage.BackColor = System.Drawing.SystemColors.Window; this.LegendConflictsImage.Image = global::CKAN.GUI.EmbeddedImages.alert; - this.LegendConflictsImage.Location = new System.Drawing.Point(6, 93); + this.LegendConflictsImage.Location = new System.Drawing.Point(6, 111); this.LegendConflictsImage.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; this.LegendConflictsImage.ClientSize = new System.Drawing.Size(14, 14); // // LegendConflictsLabel // this.LegendConflictsLabel.AutoSize = true; - this.LegendConflictsLabel.Location = new System.Drawing.Point(24, 93); + this.LegendConflictsLabel.Location = new System.Drawing.Point(24, 111); resources.ApplyResources(this.LegendConflictsLabel, "LegendConflictsLabel"); // // ReverseRelationshipsCheckbox @@ -181,6 +189,7 @@ private void InitializeComponent() // Relationships // this.Controls.Add(this.DependsGraphTree); + this.Controls.Add(this.LegendInstalledLabel); this.Controls.Add(this.LegendProvidesImage); this.Controls.Add(this.LegendProvidesLabel); this.Controls.Add(this.LegendDependsImage); @@ -205,6 +214,7 @@ private void InitializeComponent() private System.Windows.Forms.ToolTip ToolTip; private System.Windows.Forms.TreeView DependsGraphTree; + private System.Windows.Forms.Label LegendInstalledLabel; private System.Windows.Forms.PictureBox LegendProvidesImage; private System.Windows.Forms.Label LegendProvidesLabel; private System.Windows.Forms.PictureBox LegendDependsImage; diff --git a/GUI/Controls/ModInfoTabs/Relationships.cs b/GUI/Controls/ModInfoTabs/Relationships.cs index 913bafcb1..a5e042873 100644 --- a/GUI/Controls/ModInfoTabs/Relationships.cs +++ b/GUI/Controls/ModInfoTabs/Relationships.cs @@ -141,8 +141,15 @@ private void _UpdateModDependencyGraph(CkanModule module) }; DependsGraphTree.Nodes.Add(root); AddChildren(registry, root); - DependsGraphTree.EndUpdate(); root.Expand(); + // Expand virtual depends nodes + foreach (var node in root.Nodes.OfType() + .Where(nd => nd.Nodes.Count > 0 + && nd.ImageIndex == (int)RelationshipType.Depends + 1)) + { + node.Expand(); + } + DependsGraphTree.EndUpdate(); } private void BeforeExpand(object sender, TreeViewCancelEventArgs args) @@ -270,8 +277,18 @@ private IEnumerable ForwardRelationships(IRegistryQuerier registry, Ck // Then give up and note the name without a module ?? nonindexedNode(dependency, relationship)))); - private TreeNode findDependencyShallow(IRegistryQuerier registry, RelationshipDescriptor relDescr, RelationshipType relationship, GameVersionCriteria crit) + private TreeNode findDependencyShallow(IRegistryQuerier registry, + RelationshipDescriptor relDescr, + RelationshipType relationship, + GameVersionCriteria crit) { + var childNodes = relDescr.LatestAvailableWithProvides( + registry, crit, + // Ignore conflicts with installed mods + new List()) + .Select(dep => indexedNode(registry, dep, relationship, relDescr, crit)) + .ToList(); + // Check if this dependency is installed if (relDescr.MatchesAny(registry.InstalledModules.Select(im => im.Module).ToList(), registry.InstalledDlls.ToHashSet(), @@ -279,34 +296,43 @@ private TreeNode findDependencyShallow(IRegistryQuerier registry, RelationshipDe registry.InstalledDlc, out CkanModule matched)) { - return matched != null - ? indexedNode(registry, matched, relationship, relDescr, crit) - : nonModuleNode(relDescr, null, relationship); + if (matched == null) + { + childNodes.Add(nonModuleNode(relDescr, null, relationship)); + } + else + { + var newNode = indexedNode(registry, matched, relationship, relDescr, crit); + if (childNodes.FindIndex(nd => (nd.Tag as CkanModule)?.identifier == matched.identifier) + is int index && index != -1) + { + // Replace the latest provider with the installed version + childNodes[index] = newNode; + } + else + { + childNodes.Add(newNode); + } + } } - // Find modules that satisfy this dependency - List dependencyModules = relDescr.LatestAvailableWithProvides( - registry, crit, - // Ignore conflicts with installed mods - new List()); - if (dependencyModules.Count == 0) + if (childNodes.Count == 0) { // Nothing found, don't return a node return null; } - else if (dependencyModules.Count == 1 - && relDescr.ContainsAny(new string[] { dependencyModules[0].identifier })) + else if (childNodes.Count == 1 + && childNodes[0].Tag is CkanModule module + && relDescr.ContainsAny(new string[] { module.identifier })) { // Only one exact match module, return a simple node - return indexedNode(registry, dependencyModules[0], relationship, relDescr, crit); + return childNodes[0]; } else { // Several found or not same id, return a "provides" node - return providesNode(relDescr.ToString(), - relationship, - dependencyModules.Select(dep => indexedNode( - registry, dep, relationship, relDescr, crit))); + return providesNode(relDescr.ToString(), relationship, + childNodes.ToArray()); } } @@ -326,18 +352,27 @@ private IEnumerable ReverseRelationships(IRegistryQuerier registry, Ck .Select(r => indexedNode(registry, otherMod, relationship, r, crit))))); } - private TreeNode providesNode(string identifier, RelationshipType relationship, IEnumerable children) + private TreeNode providesNode(string identifier, + RelationshipType relationship, + TreeNode[] children) { int icon = (int)relationship + 1; - return new TreeNode(string.Format(Properties.Resources.ModInfoVirtual, identifier), icon, icon, children.ToArray()) + var node = new TreeNode(string.Format(Properties.Resources.ModInfoVirtual, + identifier), + icon, icon, children) { Name = identifier, ToolTipText = relationship.Localize(), ForeColor = SystemColors.GrayText, }; + return node; } - private TreeNode indexedNode(IRegistryQuerier registry, CkanModule module, RelationshipType relationship, RelationshipDescriptor relDescr, GameVersionCriteria crit) + private TreeNode indexedNode(IRegistryQuerier registry, + CkanModule module, + RelationshipType relationship, + RelationshipDescriptor relDescr, + GameVersionCriteria crit) { int icon = (int)relationship + 1; bool missingDLC = module.IsDLC && !registry.InstalledDlc.ContainsKey(module.identifier); @@ -350,26 +385,37 @@ private TreeNode indexedNode(IRegistryQuerier registry, CkanModule module, Relat ToolTipText = $"{relationship.Localize()} {relDescr}", Tag = module, ForeColor = (compatible && !missingDLC) - ? SystemColors.WindowText - : Color.Red, + ? SystemColors.WindowText + : Color.Red, + NodeFont = new Font(DependsGraphTree.Font, + registry.IsInstalled(module.identifier, false) + ? FontStyle.Bold + : FontStyle.Regular), }; } - private TreeNode nonModuleNode(RelationshipDescriptor relDescr, ModuleVersion version, RelationshipType relationship) + private TreeNode nonModuleNode(RelationshipDescriptor relDescr, + ModuleVersion version, + RelationshipType relationship) { int icon = (int)relationship + 1; return new TreeNode($"{relDescr} {version}", icon, icon) { Name = relDescr.ToString(), - ToolTipText = relationship.Localize() + ToolTipText = relationship.Localize(), + NodeFont = new Font(DependsGraphTree.Font, + FontStyle.Bold), }; } - private TreeNode nonindexedNode(RelationshipDescriptor relDescr, RelationshipType relationship) + private TreeNode nonindexedNode(RelationshipDescriptor relDescr, + RelationshipType relationship) { // Completely nonexistent dependency, e.g. "AJE" int icon = (int)relationship + 1; - return new TreeNode(string.Format(Properties.Resources.ModInfoNotIndexed, relDescr.ToString()), icon, icon) + return new TreeNode(string.Format(Properties.Resources.ModInfoNotIndexed, + relDescr.ToString()), + icon, icon) { Name = relDescr.ToString(), ToolTipText = relationship.Localize(), diff --git a/GUI/Controls/ModInfoTabs/Relationships.resx b/GUI/Controls/ModInfoTabs/Relationships.resx index e8ff33ff8..931c7a0af 100644 --- a/GUI/Controls/ModInfoTabs/Relationships.resx +++ b/GUI/Controls/ModInfoTabs/Relationships.resx @@ -117,6 +117,7 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + Installed Provides Depends Recommends