diff --git a/Source/LibationAvalonia/Views/ProcessQueueControl.axaml b/Source/LibationAvalonia/Views/ProcessQueueControl.axaml index bbb66db3..07b70824 100644 --- a/Source/LibationAvalonia/Views/ProcessQueueControl.axaml +++ b/Source/LibationAvalonia/Views/ProcessQueueControl.axaml @@ -82,6 +82,18 @@ Maximum="{Binding MaxAllowedConcurrentDownloads}" Increment="1" FormatString="0" /> + + Configuration.MinConcurrentDownloads; /// - /// The spinner's upper bound: what this machine can usefully manage, but never below what is - /// already stored. A spinner whose maximum sits under the stored value coerces its displayed - /// value down to the maximum and, being two-way, writes that back - which would overwrite an 8 - /// chosen on a larger machine with a 2 just for opening the panel on a smaller one. Raising the - /// bound to meet the stored value leaves the setting alone; lowering it is still the user's to do. + /// The spinner's upper bound: the flat hard limit, so both UIs agree and the bound is the same + /// number on every machine. Bounding it by machine capability instead makes the control lie in two + /// directions at once - a spinner whose maximum sits under the stored value coerces its display + /// down and, being two-way, writes that back, so opening the panel on a smaller machine would + /// overwrite an 8 chosen on a larger one; while raising the bound to meet the stored value leaves + /// a stored 8 showing 8 on a two-core box that will only ever run 2. Capability is applied where + /// it actually bites, in , and + /// says so on screen rather than leaving the two to disagree in silence. /// - public int MaxAllowedConcurrentDownloads - => Math.Max(Configuration.MaxAllowedConcurrentDownloads, MaxConcurrentDownloads); + public int MaxAllowedConcurrentDownloads => Configuration.ConcurrentDownloadsHardLimit; + + /// + /// Sits beside the spinner when this machine cannot deliver the number the user chose, and is + /// null when it can. Closes the gap left by bounding the control at the hard limit: the setting + /// keeps saying what was asked for, and this says what will happen. + /// + public string? ConcurrencyHint + => EffectiveConcurrentDownloads < MaxConcurrentDownloads + ? $"({EffectiveConcurrentDownloads} on this machine)" + : null; public string? RunningTime { get => field; set => RaiseAndSetIfChanged(ref field, value); } public bool ProgressBarVisible { get => field; set => RaiseAndSetIfChanged(ref field, value); } public bool AnyCompleted => CompletedCount > 0; diff --git a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs index 6fc3dd99..eda196f5 100644 --- a/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs +++ b/Source/LibationWinForms/ProcessQueue/ProcessQueueControl.cs @@ -41,6 +41,9 @@ internal partial class ProcessQueueControl : UserControl autoScrollChk.CheckedChanged += (s, e) => ViewModel.AutoScrollQueue = autoScrollChk.Checked; concurrencyNum.Minimum = ViewModel.MinConcurrentDownloads; + // The flat hard limit, not this machine's capability - the same bound Chardonnay uses. A + // maximum below the stored value would coerce the display down and write it back, losing an 8 + // chosen elsewhere just for opening the panel here. concurrencyNum.Maximum = ViewModel.MaxAllowedConcurrentDownloads; concurrencyNum.ValueChanged += (s, e) => ViewModel.MaxConcurrentDownloads = (int)concurrencyNum.Value; ProcessQueue_PropertyChanged(this, new PropertyChangedEventArgs(null)); @@ -141,8 +144,28 @@ internal partial class ProcessQueueControl : UserControl autoScrollChk.Checked = ViewModel.AutoScrollQueue; if (e.PropertyName is null or nameof(ViewModel.MaxConcurrentDownloads)) concurrencyNum.Value = ViewModel.MaxConcurrentDownloads; + if (e.PropertyName is null or nameof(ViewModel.ConcurrencyHint)) + setConcurrencyHint(); + + // The settings table is full at three columns and the panel has no width to spare, so this + // says what Chardonnay says in its spare column, in a tooltip instead of another label. + void setConcurrencyHint() + { + const string what = "How many books download and decrypt at the same time. 1 downloads one at a time."; + concurrencyHintToolTip.SetToolTip( + concurrencyNum, + ViewModel.ConcurrencyHint is string hint + ? $"{what}\r\nOnly {hint.Trim('(', ')')} - this machine has fewer processors to decrypt them." + : what); + } } + /// + /// Carries . Built here rather than in the + /// designer to keep the generated file out of the diff. + /// + private readonly ToolTip concurrencyHintToolTip = new(); + /// /// View notified the model that a button was clicked ///