Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Languages/lang_bn.json
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@
"Local": "স্থানীয়",
"OK": "ঠিক আছে",
"Last checked: {0}": "সর্বশেষ পরীক্ষা করা হয়েছে: {0}",
"{0} packages were found, {1} of which match the specified filters.": "{1} প্যাকেজ পাওয়া গেছে যার মধ্যে {0}টি নির্দিষ্ট ফিল্টারগুলির সাথে মেলে।",
"{0} packages were found, {1} of which match the specified filters.": "{0} প্যাকেজ পাওয়া গেছে যার মধ্যে {1}টি নির্দিষ্ট ফিল্টারগুলির সাথে মেলে।",
"{0} selected": "{0} নির্বাচিত",
"(Last checked: {0})": "(শেষ পরীক্ষা করা হয়েছেঃ {0})",
"All packages selected": "সমস্ত প্যাকেজ নির্বাচিত",
Expand Down
2 changes: 1 addition & 1 deletion src/Languages/lang_ca.json
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@
"Local": "Local",
"OK": "D'acord",
"Last checked: {0}": "Última comprovació: {0}",
"{0} packages were found, {1} of which match the specified filters.": "S'han trobat {1} paquets, {0} dels quals s'ajusten als filtres establerts.",
"{0} packages were found, {1} of which match the specified filters.": "S'han trobat {0} paquets, {1} dels quals s'ajusten als filtres establerts.",
"{0} selected": "{0} seleccionats",
"(Last checked: {0})": "(Comprovat per darrer cop: {0})",
"All packages selected": "Tots els paquets seleccionats",
Expand Down
2 changes: 1 addition & 1 deletion src/Languages/lang_gu.json
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@
"Local": "સ્થાનિક",
"OK": "ઠીક છે",
"Last checked: {0}": "છેલ્લે તપાસ્યું: {0}",
"{0} packages were found, {1} of which match the specified filters.": "{1} packages મળ્યા, જેમાંથી {0} નિર્દિષ્ટ filters સાથે મેળ ખાતા હતા.",
"{0} packages were found, {1} of which match the specified filters.": "{0} packages મળ્યા, જેમાંથી {1} નિર્દિષ્ટ filters સાથે મેળ ખાતા હતા.",
"{0} selected": "{0} પસંદ થયું",
"(Last checked: {0})": "(છેલ્લે ચકાસાયેલ: {0})",
"All packages selected": "બધા પેકેજો પસંદ કરવામાં આવ્યા",
Expand Down
2 changes: 1 addition & 1 deletion src/Languages/lang_nb.json
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@
"Local": "Lokal",
"OK": "Greit",
"Last checked: {0}": "Sist sjekket: {0}",
"{0} packages were found, {1} of which match the specified filters.": "{1} pakker ble funnet, hvorav {0} samsvarte med filtrene dine.",
"{0} packages were found, {1} of which match the specified filters.": "{0} pakker ble funnet, hvorav {1} samsvarte med filtrene dine.",
"{0} selected": "{0} valgt",
"(Last checked: {0})": "(Sist sjekket: {0})",
"All packages selected": "Alle pakker valgt",
Expand Down
2 changes: 1 addition & 1 deletion src/Languages/lang_sv.json
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@
"Local": "Lokal",
"OK": "OK",
"Last checked: {0}": "Senast kontrollerad: {0}",
"{0} packages were found, {1} of which match the specified filters.": "{1} paket hittades, varav {0} matchade filtret",
"{0} packages were found, {1} of which match the specified filters.": "{0} paket hittades, varav {1} matchade filtret",
"{0} selected": "{0} valda",
"(Last checked: {0})": "(Kontrollerades senast: {0})",
"All packages selected": "Alla paket valda",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,46 @@ partial void OnIsFilterPaneOpenChanged(bool value)
[ObservableProperty] private string _pageTitle = "";
[ObservableProperty] private string _pageIconPath = "";
[ObservableProperty] private string _subtitle = "";

// Shown after the subtitle in a secondary tone: when the list was last checked, then the page suffix.
[ObservableProperty] private string _subtitleDetail = "";

// The whole subtitle as one sentence, for screen readers.
[ObservableProperty] private string _subtitleAccessibleText = "";

/// <summary>The subtitle cut around its numbers, so the page can draw them stronger than the words.</summary>
[ObservableProperty] private IReadOnlyList<SubtitleSegment> _subtitleSegments = [];

// The sentence is formatted with the real integers, so ICU plural selectors keep working (ru, lt, et, sq);
// the numbers are then found in the formatted text, wherever the translation placed them.
private void SetSubtitle(string text, params int[] values)
{
var segments = new List<SubtitleSegment>();
if (values.Length > 0)
{
string alternatives = string.Join('|', values
.Select(v => v.ToString(CultureInfo.CurrentCulture))
.Distinct()
.OrderByDescending(v => v.Length)
.Select(System.Text.RegularExpressions.Regex.Escape));
int last = 0;
foreach (System.Text.RegularExpressions.Match match in
System.Text.RegularExpressions.Regex.Matches(text, $@"(?<!\d)(?:{alternatives})(?!\d)"))
{
if (match.Index > last) segments.Add(new SubtitleSegment(text[last..match.Index], false));
segments.Add(new SubtitleSegment(match.Value, true));
last = match.Index + match.Length;
}
if (last < text.Length) segments.Add(new SubtitleSegment(text[last..], false));
}
else
{
segments.Add(new SubtitleSegment(text, false));
}

SubtitleSegments = segments;
Subtitle = text;
}
[ObservableProperty] private bool _isLoading;
[ObservableProperty] private bool _backgroundTextVisible;
[ObservableProperty] private bool _loadingImageVisible;
Expand Down Expand Up @@ -1059,30 +1099,34 @@ public void UpdateSubtitle()
{
if (Loader.IsLoading || (LoadsOnStart && Loader.HasPendingInitialLoad))
{
Subtitle = _stillLoadingSubtitle;
SetSubtitle(_stillLoadingSubtitle);
SubtitleDetail = "";
SubtitleAccessibleText = Subtitle;
return;
}

SubtitleDetail = (_showLastCheckedTime
? " " + CoreTools.Translate("(Last checked: {0})", _lastLoadTime.ToString(CultureInfo.CurrentCulture))
: "") + SubtitleSuffix;

if (Loader.Any())
{
// {0} is everything the managers returned, {1} what the filters keep, as the sentence reads
int selected = FilteredPackages.GetCheckedPackages().Count;
string r = CoreTools.Translate(
int found = _wrappedPackages.Count;
int matching = FilteredPackages.Count;
SetSubtitle(CoreTools.Translate(
"{0} packages were found, {1} of which match the specified filters.",
FilteredPackages.Count,
_wrappedPackages.Count
) + " (" + CoreTools.Translate("{0} selected", selected) + ")";

if (_showLastCheckedTime)
r += " " + CoreTools.Translate("(Last checked: {0})", _lastLoadTime.ToString(CultureInfo.CurrentCulture));

Subtitle = r + SubtitleSuffix;
found,
matching
) + " (" + CoreTools.Translate("{0} selected", selected) + ")", found, matching, selected);
}
else
{
Subtitle = _noPackagesSubtitleBase + (_showLastCheckedTime
? " " + CoreTools.Translate("(Last checked: {0})", _lastLoadTime.ToString(CultureInfo.CurrentCulture))
: "") + SubtitleSuffix;
SetSubtitle(_noPackagesSubtitleBase);
}

SubtitleAccessibleText = Subtitle + SubtitleDetail;
}

// ─── Commands ─────────────────────────────────────────────────────────────
Expand Down Expand Up @@ -1202,3 +1246,6 @@ public static bool NameOrIdExactMatch(IPackage pkg, string q, List<Func<string,
}
}
}

/// <summary>A piece of the page subtitle; <paramref name="IsValue"/> marks a number.</summary>
public readonly record struct SubtitleSegment(string Text, bool IsValue);
Original file line number Diff line number Diff line change
Expand Up @@ -270,14 +270,14 @@
FontFamily="Segoe UI Variable Display"
TextWrapping="Wrap"
Text="{Binding PageTitle}"/>
<!-- Words and last-check time in the secondary text colour; the numbers are added stronger from code (UpdateSubtitleInlines) -->
<TextBlock x:Name="MainSubtitle"
MaxHeight="40"
HorizontalAlignment="Left"
FontSize="12"
FontWeight="Normal"
Opacity="0.6"
TextWrapping="Wrap"
Text="{Binding Subtitle}"/>
Foreground="{DynamicResource TextFillColorSecondaryBrush}"
TextWrapping="Wrap"/>
</StackPanel>

</Grid>
Expand Down Expand Up @@ -717,7 +717,7 @@
ItemsSource="{Binding FilteredPackages}"
automation:AutomationProperties.AccessibilityView="Control"
automation:AutomationProperties.Name="{Binding PageTitle}"
automation:AutomationProperties.HelpText="{Binding Subtitle}"
automation:AutomationProperties.HelpText="{Binding SubtitleAccessibleText}"
AutoGenerateColumns="False"
CanUserReorderColumns="True"
CanUserResizeColumns="True"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Avalonia.Automation;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Controls.Documents;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Interactivity;
Expand Down Expand Up @@ -84,7 +85,13 @@
SyncFiltersButtonName();
UpdateFilterPaneColumn(ViewModel.IsFilterPaneOpen, animate: true);
}
// Set last by UpdateSubtitle, so the segments and the detail are both current by then
if (args.PropertyName is nameof(PackagesPageViewModel.SubtitleAccessibleText))
{
UpdateSubtitleInlines();
}
};
UpdateSubtitleInlines();
SyncFiltersButtonName();
SyncOrderByButtonName();

Expand Down Expand Up @@ -411,6 +418,28 @@
CoreTools.Translate("{0}: {1}, {2}", CoreTools.Translate("Order by"), ViewModel.SortFieldName, direction));
}

// The numbers are the information that changes: semibold in the body text colour, the words keep the secondary tone
private void UpdateSubtitleInlines()
{
var inlines = new InlineCollection();
foreach (SubtitleSegment segment in ViewModel.SubtitleSegments)
{
var run = new Run(segment.Text);
if (segment.IsValue)
{
run.FontWeight = FontWeight.SemiBold;
run.Bind(TextElement.ForegroundProperty, this.GetResourceObservable("TextFillColorPrimaryBrush"));
}
inlines.Add(run);
}

if (ViewModel.SubtitleDetail.Length > 0)
inlines.Add(new Run(ViewModel.SubtitleDetail));

MainSubtitle.Inlines = inlines;
AutomationProperties.SetName(MainSubtitle, ViewModel.SubtitleAccessibleText);
}

private void UpdateSortMenuChecks()
{
OrderByName_Menu.Icon = Check(ViewModel.SortFieldIndex == 0);
Expand Down Expand Up @@ -848,7 +877,7 @@

// Guard against spreadsheet formula injection (CWE-1236): package metadata is
// external, and a value starting with one of these executes as a formula in Excel/Sheets.
if (field.Length > 0 && "=+-@\t\r".IndexOf(field[0]) >= 0)

Check warning on line 880 in src/UniGetUI.Avalonia/Views/SoftwarePages/AbstractPackagesPage.axaml.cs

View workflow job for this annotation

GitHub Actions / test-codebase

Use 'string.Contains' instead of 'string.IndexOf' to improve readability (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2249)
field = "'" + field;

if (field.IndexOfAny(['"', ',', '\n', '\r']) >= 0)
Expand Down
Loading