Skip to content
Merged
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
3 changes: 3 additions & 0 deletions src/Filee.App/Assets/i18n/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@
"crop.aspect_title": "نسبة الأبعاد:",
"crop.aspect_free": "حرة",
"crop.aspect_original": "الأصلية",
"crop.aspect_custom": "نسبة مخصّصة (اكتب العرض والارتفاع بجانب زر النسبة)",
"crop.custom_width": "العرض",
"crop.custom_height": "الارتفاع",
"crop.page_counter": "{0} من {1}",
"crop.save_next": "اقتصاص والتالي",
"crop.swap": "تبديل الوضع الطولي والعرضي (X)",
Expand Down
3 changes: 3 additions & 0 deletions src/Filee.App/Assets/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@
"crop.aspect_title": "Seitenverhältnis:",
"crop.aspect_free": "Frei",
"crop.aspect_original": "Original",
"crop.aspect_custom": "Eigenes Verhältnis (B und H neben der Verhältnis-Taste eingeben)",
"crop.custom_width": "Breite (B)",
"crop.custom_height": "Höhe (H)",
"crop.page_counter": "{0} von {1}",
"crop.save_next": "Zuschneiden & Weiter",
"crop.swap": "Hoch- und Querformat tauschen (X)",
Expand Down
3 changes: 3 additions & 0 deletions src/Filee.App/Assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@
"crop.aspect_title": "Aspect:",
"crop.aspect_free": "Free",
"crop.aspect_original": "Original",
"crop.aspect_custom": "Custom ratio (type W and H beside the ratio button)",
"crop.custom_width": "Width (W)",
"crop.custom_height": "Height (H)",
"crop.page_counter": "{0} of {1}",
"crop.save_next": "Crop & Next",
"crop.swap": "Swap portrait and landscape (X)",
Expand Down
3 changes: 3 additions & 0 deletions src/Filee.App/Assets/i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@
"crop.aspect_title": "Proporción:",
"crop.aspect_free": "Libre",
"crop.aspect_original": "Original",
"crop.aspect_custom": "Proporción personalizada (escribe An y Al junto al botón de proporción)",
"crop.custom_width": "Ancho (An)",
"crop.custom_height": "Alto (Al)",
"crop.page_counter": "{0} de {1}",
"crop.save_next": "Recortar y seguir",
"crop.swap": "Cambiar entre vertical y horizontal (X)",
Expand Down
3 changes: 3 additions & 0 deletions src/Filee.App/Assets/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@
"crop.aspect_title": "Proportions :",
"crop.aspect_free": "Libre",
"crop.aspect_original": "Original",
"crop.aspect_custom": "Rapport personnalisé (saisissez L et H à côté du bouton du rapport)",
"crop.custom_width": "Largeur (L)",
"crop.custom_height": "Hauteur (H)",
"crop.page_counter": "{0} sur {1}",
"crop.save_next": "Recadrer et suivant",
"crop.swap": "Inverser portrait et paysage (X)",
Expand Down
3 changes: 3 additions & 0 deletions src/Filee.App/Assets/i18n/hi.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@
"crop.aspect_title": "अनुपात:",
"crop.aspect_free": "मुक्त",
"crop.aspect_original": "मूल",
"crop.aspect_custom": "अपना अनुपात (अनुपात बटन के पास चौड़ाई और ऊँचाई लिखें)",
"crop.custom_width": "चौड़ाई",
"crop.custom_height": "ऊँचाई",
"crop.page_counter": "{1} में से {0}",
"crop.save_next": "क्रॉप करके अगला",
"crop.swap": "खड़ा और आड़ा बदलें (X)",
Expand Down
3 changes: 3 additions & 0 deletions src/Filee.App/Assets/i18n/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@
"crop.aspect_title": "縦横比:",
"crop.aspect_free": "自由",
"crop.aspect_original": "元の比率",
"crop.aspect_custom": "カスタム比率 (比率ボタンの横に幅と高さを入力)",
"crop.custom_width": "幅",
"crop.custom_height": "高さ",
"crop.page_counter": "{0}/{1}",
"crop.save_next": "トリミングして次へ",
"crop.swap": "縦向きと横向きを切り替え (X)",
Expand Down
3 changes: 3 additions & 0 deletions src/Filee.App/Assets/i18n/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@
"crop.aspect_title": "비율:",
"crop.aspect_free": "자유",
"crop.aspect_original": "원본",
"crop.aspect_custom": "직접 입력 비율 (비율 버튼 옆에 너비와 높이를 입력)",
"crop.custom_width": "너비",
"crop.custom_height": "높이",
"crop.page_counter": "{0}/{1}",
"crop.save_next": "자르고 다음으로",
"crop.swap": "세로/가로 바꾸기 (X)",
Expand Down
3 changes: 3 additions & 0 deletions src/Filee.App/Assets/i18n/ne.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@
"crop.aspect_title": "अनुपात:",
"crop.aspect_free": "स्वतन्त्र",
"crop.aspect_original": "मूल",
"crop.aspect_custom": "आफ्नै अनुपात (अनुपात बटनको छेउमा चौडाइ र उचाइ लेख्नुहोस्)",
"crop.custom_width": "चौडाइ",
"crop.custom_height": "उचाइ",
"crop.page_counter": "{1} मध्ये {0}",
"crop.save_next": "क्रप गरी अर्कोमा जानुहोस्",
"crop.swap": "ठाडो र तेर्सो बदल्नुहोस् (X)",
Expand Down
3 changes: 3 additions & 0 deletions src/Filee.App/Assets/i18n/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@
"crop.aspect_title": "Proporcje:",
"crop.aspect_free": "Dowolne",
"crop.aspect_original": "Oryginalne",
"crop.aspect_custom": "Własne proporcje (wpisz S i W obok przycisku proporcji)",
"crop.custom_width": "Szerokość (S)",
"crop.custom_height": "Wysokość (W)",
"crop.page_counter": "{0} z {1}",
"crop.save_next": "Kadruj i dalej",
"crop.swap": "Zamień pion i poziom (X)",
Expand Down
3 changes: 3 additions & 0 deletions src/Filee.App/Assets/i18n/pt-BR.json
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,9 @@
"crop.aspect_title": "Proporção:",
"crop.aspect_free": "Livre",
"crop.aspect_original": "Original",
"crop.aspect_custom": "Proporção personalizada (digite L e A ao lado do botão de proporção)",
"crop.custom_width": "Largura (L)",
"crop.custom_height": "Altura (A)",
"crop.page_counter": "{0} de {1}",
"crop.save_next": "Recortar e avançar",
"crop.swap": "Trocar retrato e paisagem (X)",
Expand Down
3 changes: 3 additions & 0 deletions src/Filee.App/Assets/i18n/pt-PT.json
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@
"crop.aspect_title": "Proporção:",
"crop.aspect_free": "Livre",
"crop.aspect_original": "Original",
"crop.aspect_custom": "Proporção personalizada (escreva L e A ao lado do botão de proporção)",
"crop.custom_width": "Largura (L)",
"crop.custom_height": "Altura (A)",
"crop.page_counter": "{0} de {1}",
"crop.save_next": "Recortar e avançar",
"crop.swap": "Alternar entre vertical e horizontal (X)",
Expand Down
3 changes: 3 additions & 0 deletions src/Filee.App/Assets/i18n/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@
"crop.aspect_title": "Пропорции:",
"crop.aspect_free": "Произвольные",
"crop.aspect_original": "Исходные",
"crop.aspect_custom": "Своё соотношение (введите Ш и В рядом с кнопкой соотношения)",
"crop.custom_width": "Ширина (Ш)",
"crop.custom_height": "Высота (В)",
"crop.page_counter": "{0} из {1}",
"crop.save_next": "Обрезать и дальше",
"crop.swap": "Поменять книжную и альбомную (X)",
Expand Down
3 changes: 3 additions & 0 deletions src/Filee.App/Assets/i18n/th.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@
"crop.aspect_title": "สัดส่วน:",
"crop.aspect_free": "อิสระ",
"crop.aspect_original": "ต้นฉบับ",
"crop.aspect_custom": "อัตราส่วนกำหนดเอง (พิมพ์กว้างและสูงข้างปุ่มอัตราส่วน)",
"crop.custom_width": "กว้าง",
"crop.custom_height": "สูง",
"crop.page_counter": "{0} จาก {1}",
"crop.save_next": "ครอบตัดและต่อไป",
"crop.swap": "สลับแนวตั้งและแนวนอน (X)",
Expand Down
3 changes: 3 additions & 0 deletions src/Filee.App/Assets/i18n/tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,9 @@
"crop.aspect_title": "Oran:",
"crop.aspect_free": "Serbest",
"crop.aspect_original": "Özgün",
"crop.aspect_custom": "Özel oran (oran düğmesinin yanına G ve Y yazın)",
"crop.custom_width": "Genişlik (G)",
"crop.custom_height": "Yükseklik (Y)",
"crop.page_counter": "{0} / {1}",
"crop.save_next": "Kırp ve ilerle",
"crop.swap": "Dikey ve yatayı değiştir (X)",
Expand Down
3 changes: 3 additions & 0 deletions src/Filee.App/Assets/i18n/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@
"crop.aspect_title": "比例:",
"crop.aspect_free": "自由",
"crop.aspect_original": "原始比例",
"crop.aspect_custom": "自定义比例(在比例按钮旁输入宽和高)",
"crop.custom_width": "宽",
"crop.custom_height": "高",
"crop.page_counter": "{0}/{1}",
"crop.save_next": "裁剪并下一张",
"crop.swap": "切换纵向与横向 (X)",
Expand Down
3 changes: 3 additions & 0 deletions src/Filee.App/Assets/i18n/zh-TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,9 @@
"crop.aspect_title": "比例:",
"crop.aspect_free": "自由",
"crop.aspect_original": "原始比例",
"crop.aspect_custom": "自訂比例(在比例按鈕旁輸入寬和高)",
"crop.custom_width": "寬",
"crop.custom_height": "高",
"crop.page_counter": "{0}/{1}",
"crop.save_next": "裁剪並下一張",
"crop.swap": "切換縱向與橫向 (X)",
Expand Down
21 changes: 14 additions & 7 deletions src/Filee.App/Controls/AspectDial.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ namespace Filee.App.Controls;
public sealed class AspectDial : Panel
{
public static readonly StyledProperty<double> OuterRadiusProperty =
AvaloniaProperty.Register<AspectDial, double>(nameof(OuterRadius), 118);
AvaloniaProperty.Register<AspectDial, double>(nameof(OuterRadius), 132); // room for eight slices' names (#542)

public static readonly StyledProperty<double> InnerRadiusProperty =
AvaloniaProperty.Register<AspectDial, double>(nameof(InnerRadius), 52);
Expand All @@ -36,20 +36,25 @@ public sealed class AspectDial : Panel
public static readonly StyledProperty<double> OriginalRatioProperty =
AvaloniaProperty.Register<AspectDial, double>(nameof(OriginalRatio));

/// <summary>Width / height typed for the custom slice (#542): its outline.</summary>
public static readonly StyledProperty<double> CustomRatioProperty =
AvaloniaProperty.Register<AspectDial, double>(nameof(CustomRatio));

public double OuterRadius { get => GetValue(OuterRadiusProperty); set => SetValue(OuterRadiusProperty, value); }
public double InnerRadius { get => GetValue(InnerRadiusProperty); set => SetValue(InnerRadiusProperty, value); }
public CornerRadius CornerRadius { get => GetValue(CornerRadiusProperty); set => SetValue(CornerRadiusProperty, value); }
public string? SelectedMode { get => GetValue(SelectedModeProperty); set => SetValue(SelectedModeProperty, value); }
public bool IsPortrait { get => GetValue(IsPortraitProperty); set => SetValue(IsPortraitProperty, value); }
public double OriginalRatio { get => GetValue(OriginalRatioProperty); set => SetValue(OriginalRatioProperty, value); }
public double CustomRatio { get => GetValue(CustomRatioProperty); set => SetValue(CustomRatioProperty, value); }

/// <summary>Gap between slices, as on the donut.</summary>
public const double Gap = 4;

static AspectDial()
{
AffectsArrange<AspectDial>(OuterRadiusProperty, InnerRadiusProperty, CornerRadiusProperty, SelectedModeProperty,
IsPortraitProperty, OriginalRatioProperty);
IsPortraitProperty, OriginalRatioProperty, CustomRatioProperty);
AffectsMeasure<AspectDial>(OuterRadiusProperty);
}

Expand Down Expand Up @@ -93,7 +98,7 @@ protected override Size ArrangeOverride(Size finalSize)
{
var segment = segments[i];
segment.Place(center, i, segments.Count, InnerRadius, OuterRadius, Math.Clamp(CornerRadius.TopLeft, 0, 28) * 0.5);
segment.Show(SelectedMode == segment.Mode, IsPortrait, OriginalRatio);
segment.Show(SelectedMode == segment.Mode, IsPortrait, OriginalRatio, CustomRatio);
segment.Arrange(new Rect(finalSize));
}

Expand Down Expand Up @@ -141,6 +146,7 @@ public sealed class RingSegment : Button, ICustomHitTest
private Geometry? _wedge;
private bool _portrait;
private double _originalRatio;
private double _customRatio;

/// <summary>Whether this slice's ratio is the one in use.</summary>
public bool IsSelected { get; private set; }
Expand Down Expand Up @@ -170,12 +176,13 @@ internal void Place(Point center, int index, int count, double inner, double out
InvalidateVisual();
}

internal void Show(bool selected, bool portrait, double originalRatio)
internal void Show(bool selected, bool portrait, double originalRatio, double customRatio = 0)
{
var label = Label;
IsSelected = selected;
_portrait = portrait;
_originalRatio = originalRatio;
_customRatio = customRatio;
PseudoClasses.Set(":selected", selected);
if (label != Label || AutomationProperties.GetName(this) is null or "")
AutomationProperties.SetName(this, Label);
Expand Down Expand Up @@ -237,8 +244,8 @@ public override void Render(DrawingContext context)
}

/// <summary>
/// The smallest size a long one-word label shrinks to before it breaks. Ru's "Произвольные" needs 9.4 with seven
/// slices on a desktop and 8.7 on CI's runners, whose fonts differ, so there's room below that.
/// The smallest size a long one-word label shrinks to before it breaks. The dial draws in the embedded Noto Sans, where
/// Ru's "Произвольные" needs about 8.7 on seven slices; with eight, the dial is wider (OuterRadius) so it still fits.
/// </summary>
internal const double MinLabelSize = 8;

Expand All @@ -251,7 +258,7 @@ private void DrawShape(DrawingContext context, IBrush? ink, Point center)
{
if (ink is null)
return;
var ratio = Mode == "original" ? _originalRatio : CropMath.Ratio(Mode, _portrait, 0, 0);
var ratio = Mode == "original" ? _originalRatio : CropMath.Ratio(Mode, _portrait, 0, 0, _customRatio);
const double box = 15;
double w = box, h = box;
if (ratio > 0)
Expand Down
36 changes: 28 additions & 8 deletions src/Filee.App/Controls/CropMath.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,22 @@ public static class CropMath
/// <summary>The aspect dial's choices, clockwise from the top.</summary>
public static IReadOnlyList<string> AspectModes => Modes;

private static readonly string[] Modes = ["free", "original", "1:1", "5:4", "4:3", "3:2", "16:9"];
private static readonly string[] Modes = ["free", "original", "1:1", "5:4", "4:3", "3:2", "16:9", "custom"];

/// <summary>Where the mode sits on the dial, -1 for none.</summary>
public static int AspectIndex(string mode) => Array.IndexOf(Modes, mode);

/// <summary>A fixed ratio with a portrait and a landscape form (not free, the picture's own or square).</summary>
public static bool CanSwap(string mode) => mode is "5:4" or "4:3" or "3:2" or "16:9";
/// <summary>
/// A ratio with a portrait and a landscape form (not free, the picture's own or square). "custom" turns too: its
/// W × H as typed, or H × W.
/// </summary>
public static bool CanSwap(string mode) => mode is "5:4" or "4:3" or "3:2" or "16:9" or "custom";

/// <summary>Width / height of the mode, 0 for free. Portrait turns a swappable ratio around (4:3 → 3:4).</summary>
public static double Ratio(string mode, bool portrait, int originalWidth, int originalHeight)
/// <summary>
/// Width / height of the mode, 0 for free. Portrait turns a swappable ratio around (4:3 → 3:4).
/// <paramref name="custom"/> is the typed W / H of "custom" (#542).
/// </summary>
public static double Ratio(string mode, bool portrait, int originalWidth, int originalHeight, double custom = 0)
{
var ratio = mode switch
{
Expand All @@ -29,15 +35,29 @@ public static double Ratio(string mode, bool portrait, int originalWidth, int or
"4:3" => 4.0 / 3.0,
"3:2" => 3.0 / 2.0,
"16:9" => 16.0 / 9.0,
"custom" => custom,
_ => 0,
};
return portrait && CanSwap(mode) ? 1 / ratio : ratio;
return portrait && CanSwap(mode) && ratio > 0 ? 1 / ratio : ratio;
}

/// <summary>
/// The custom ratio's numbers as small as they go ("1080 × 1350" → "4:5"), turned around in portrait. Only the ratio
/// counts: resizing to exact pixels is a preset's job.
/// </summary>
public static string CustomNumbers(int width, int height, bool portrait)
{
var divisor = Gcd(Math.Max(1, width), Math.Max(1, height));
var (w, h) = (Math.Max(1, width) / divisor, Math.Max(1, height) / divisor);
return portrait ? $"{h}:{w}" : $"{w}:{h}";

static int Gcd(int a, int b) => b == 0 ? a : Gcd(b, a % b);
}

/// <summary>The numbers shown for a mode: "4:3", or "3:4" in portrait. Free and original have none.</summary>
/// <summary>The numbers shown for a mode: "4:3", or "3:4" in portrait. Free, original and custom have none here.</summary>
public static string Numbers(string mode, bool portrait)
{
if (!CanSwap(mode) || !portrait)
if (!CanSwap(mode) || !portrait || mode == "custom")
return mode;
var parts = mode.Split(':');
return $"{parts[1]}:{parts[0]}";
Expand Down
Loading
Loading