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
9 changes: 9 additions & 0 deletions src/Filee.App/Assets/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
"tray.quit": "Quit",
"tray.update": "Update to {0}",
"tray.tooltip": "Filee: hold {0} and drag files",
"close.title": "Keep Filee running?",
"close.text": "In the tray, the donut toolbar and watch folders keep working.\nYou can change this later in Settings → General.",
"close.tray": "Keep in tray",
"close.quit": "Quit",
"close.dont_ask": "Don't ask again",

"home.title": "Home",
"home.drop_title": "Drop files here",
Expand Down Expand Up @@ -178,6 +183,10 @@
"general.keep_dates": "Keep the original file dates for every conversion",
"general.keep_dates_hint": "Converted files keep the original's created and modified dates, with every preset.\nOff: only presets with \"Keep the original file dates\" do it.",
"general.check_updates": "Check for updates automatically",
"general.close_action": "When I close the window",
"general.close.Ask": "Ask every time",
"general.close.Tray": "Keep Filee running in the tray",
"general.close.Quit": "Quit Filee",
"general.history": "Conversion history",
"general.no_history": "Don't keep a history of recent conversions",
"general.clear_history": "Clear history",
Expand Down
9 changes: 9 additions & 0 deletions src/Filee.App/Assets/i18n/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
"tray.quit": "종료",
"tray.update": "{0} 버전으로 업데이트",
"tray.tooltip": "Filee: {0} 누른 채 파일 드래그",
"close.title": "Filee를 계속 실행할까요?",
"close.text": "트레이에 두면 원형 툴바와 자동 변환 폴더가 계속 동작해요.\n나중에 설정 → 일반에서 바꿀 수 있어요.",
"close.tray": "트레이에 유지",
"close.quit": "종료",
"close.dont_ask": "다시 묻지 않기",

"home.title": "홈",
"home.drop_title": "여기에 파일을 놓으세요",
Expand Down Expand Up @@ -178,6 +183,10 @@
"general.keep_dates": "모든 변환에서 원본 파일 날짜 유지",
"general.keep_dates_hint": "어떤 프리셋이든 원본의 만든 날짜·수정한 날짜를 그대로 써요.\n끄면 \"원본 파일 날짜 유지\"를 켠 프리셋만 그렇게 해요.",
"general.check_updates": "자동으로 업데이트 확인",
"general.close_action": "창을 닫을 때",
"general.close.Ask": "매번 묻기",
"general.close.Tray": "트레이에서 계속 실행",
"general.close.Quit": "Filee 종료",
"general.history": "변환 기록",
"general.no_history": "최근 변환 기록 남기지 않기",
"general.clear_history": "기록 지우기",
Expand Down
9 changes: 9 additions & 0 deletions src/Filee.App/Assets/i18n/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
"tray.quit": "退出",
"tray.update": "更新到 {0}",
"tray.tooltip": "Filee:按住 {0} 拖动文件",
"close.title": "让 Filee 继续运行吗?",
"close.text": "保留在托盘中时,环形工具栏和自动转换文件夹会继续工作。\n之后可以在 设置 → 常规 中更改。",
"close.tray": "保留在托盘",
"close.quit": "退出",
"close.dont_ask": "不再询问",

"home.title": "首页",
"home.drop_title": "将文件拖放到这里",
Expand Down Expand Up @@ -178,6 +183,10 @@
"general.keep_dates": "所有转换都保留原文件日期",
"general.keep_dates_hint": "无论使用哪个预设,转换后的文件都沿用原文件的创建和修改日期。\n关闭后,只有打开“保留原文件日期”的预设会这样做。",
"general.check_updates": "自动检查更新",
"general.close_action": "关闭窗口时",
"general.close.Ask": "每次询问",
"general.close.Tray": "在托盘中继续运行",
"general.close.Quit": "退出 Filee",
"general.history": "转换记录",
"general.no_history": "不保留最近的转换记录",
"general.clear_history": "清除记录",
Expand Down
25 changes: 24 additions & 1 deletion src/Filee.App/ViewModels/Pages/GeneralPageViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

namespace Filee.App.ViewModels.Pages;

public sealed partial class GeneralPageViewModel : ObservableObject
public sealed partial class GeneralPageViewModel : ObservableObject, IDisposable
{
private readonly UserDataStore _store;
private readonly ILocalizer _loc;
Expand Down Expand Up @@ -40,6 +40,9 @@ public GeneralPageViewModel(UserDataStore store, ILocalizer loc, IPlatformServic
_defaultSubfolder = output.SubfolderName;
_defaultFolder = output.CustomFolder;
_keepFileDates = store.Settings.KeepFileDates;
CloseChoices = Enum.GetValues<CloseAction>().Select(v => new Choice<CloseAction>(v, loc[$"general.close.{v}"])).ToList();
_closeChoice = CloseChoices.First(c => c.Value == store.Settings.CloseAction);
store.SettingsChanged += OnSettingsChanged;
_historyCount = store.History.Count;
RefreshModernMenu();
}
Expand Down Expand Up @@ -74,6 +77,26 @@ partial void OnDefaultSubfolderChanged(string value) =>

[ObservableProperty] private bool _keepFileDates;

/// <summary>What closing the main window does; its "Don't ask again" also sets it.</summary>
public IReadOnlyList<Choice<CloseAction>> CloseChoices { get; }

[ObservableProperty] private Choice<CloseAction> _closeChoice;

partial void OnCloseChoiceChanged(Choice<CloseAction> value)
{
if (_store.Settings.CloseAction != value.Value)
Save(s => s.CloseAction = value.Value);
}

// The close dialog may answer "Don't ask again" while this page stays open behind the hidden window.
private void OnSettingsChanged(object? sender, EventArgs e)
{
if (CloseChoice.Value != _store.Settings.CloseAction)
CloseChoice = CloseChoices.First(c => c.Value == _store.Settings.CloseAction);
}

public void Dispose() => _store.SettingsChanged -= OnSettingsChanged;

partial void OnKeepFileDatesChanged(bool value) => Save(s => s.KeepFileDates = value);

[ObservableProperty] private Choice<string> _language;
Expand Down
27 changes: 27 additions & 0 deletions src/Filee.App/Views/CloseAskWindow.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!-- Asked when the main window closes while AppSettings.CloseAction is Ask: keep Filee in the tray or quit, with
"Don't ask again" (Settings → General changes it later). -->
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Filee.App.Views.CloseAskWindow"
Title="Filee"
Icon="avares://Filee/Assets/Icons/filee.ico"
Width="440"
SizeToContent="Height"
CanResize="False"
WindowStartupLocation="CenterOwner"
ShowInTaskbar="False">
<StackPanel Margin="22" Spacing="14">
<StackPanel Spacing="4">
<TextBlock Classes="h2" Text="{DynamicResource close.title}" />
<TextBlock Classes="caption" TextWrapping="Wrap" Text="{DynamicResource close.text}" />
</StackPanel>
<CheckBox x:Name="DontAsk" Content="{DynamicResource close.dont_ask}" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right" Spacing="8">
<Button MinWidth="88" HorizontalContentAlignment="Center" Content="{DynamicResource common.cancel}"
Click="OnCancel" IsCancel="True" />
<Button MinWidth="88" HorizontalContentAlignment="Center" Content="{DynamicResource close.quit}" Click="OnQuit" />
<Button Classes="accent" MinWidth="88" HorizontalContentAlignment="Center" Content="{DynamicResource close.tray}"
Click="OnTray" IsDefault="True" />
</StackPanel>
</StackPanel>
</Window>
28 changes: 28 additions & 0 deletions src/Filee.App/Views/CloseAskWindow.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Filee.App.Services;
using Filee.Core.Settings;

namespace Filee.App.Views;

/// <summary>
/// "Keep Filee running?" when the main window closes. <c>ShowDialog</c> returns <see cref="CloseAction.Tray"/>,
/// <see cref="CloseAction.Quit"/>, or null when cancelled.
/// </summary>
public partial class CloseAskWindow : Window
{
public CloseAskWindow()
{
InitializeComponent();
Motion.Track(this);
}

/// <summary>"Don't ask again" was ticked: the answer becomes the setting.</summary>
public bool DontAskAgain => DontAsk.IsChecked == true;

private void OnTray(object? sender, RoutedEventArgs e) => Close(CloseAction.Tray);

private void OnQuit(object? sender, RoutedEventArgs e) => Close(CloseAction.Quit);

private void OnCancel(object? sender, RoutedEventArgs e) => Close(null);
}
67 changes: 63 additions & 4 deletions src/Filee.App/Views/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Interactivity;
using Avalonia.Threading;
using Filee.App.Services;
using Filee.App.ViewModels;
using Filee.Core.Localization;
using Filee.Core.Settings;

namespace Filee.App.Views;

public partial class MainWindow : Window
{
private bool _asking;

public MainWindow()
{
InitializeComponent();
Expand All @@ -23,14 +29,67 @@ private async void OnOpenGitHub(object? sender, RoutedEventArgs e)
private async void OnSponsor(object? sender, RoutedEventArgs e) =>
await SponsorWindow.Create(AppHost.Get<ILocalizer>()).ShowDialog(this);

/// <summary>Closing the window keeps Filee running in the tray (Quit is in the tray menu).</summary>
/// <summary>
/// Closing the window keeps Filee running in the tray, quits, or asks which (<see cref="AppSettings.CloseAction"/>).
/// </summary>
protected override void OnClosing(WindowClosingEventArgs e)
{
base.OnClosing(e);
if (e.CloseReason is WindowCloseReason.WindowClosing)
{
e.Cancel = true;
if (e.CloseReason is not WindowCloseReason.WindowClosing)
return;
e.Cancel = true;
// Close() from code only hides, as before; the setting is for the title bar's close button and Alt+F4.
if (e.IsProgrammatic)
Hide();
else
CloseAsTheUserChose();
}

/// <summary>What the user's close does: hide, quit or ask (<see cref="AppSettings.CloseAction"/>).</summary>
internal void CloseAsTheUserChose()
{
var store = AppHost.Get<UserDataStore>();
switch (store.Settings.CloseAction)
{
case CloseAction.Tray:
Hide();
break;
case CloseAction.Quit:
Quit();
break;
default:
_ = AskAsync(store);
break;
}
}

private async Task AskAsync(UserDataStore store)
{
if (_asking)
return;
_asking = true;
try
{
var dialog = new CloseAskWindow();
if (await dialog.ShowDialog<CloseAction?>(this) is not { } action)
return;
if (dialog.DontAskAgain)
{
store.Settings.CloseAction = action;
store.SaveSettings();
}
if (action == CloseAction.Quit)
Quit();
else
Hide();
}
finally
{
_asking = false;
}
}

// Posted: shutting down closes this window again, which must not happen inside its own Closing handler.
private static void Quit() => Dispatcher.UIThread.Post(() =>
(Application.Current?.ApplicationLifetime as IClassicDesktopStyleApplicationLifetime)?.Shutdown());
}
4 changes: 4 additions & 0 deletions src/Filee.App/Views/Pages/GeneralPage.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
<ComboBox ItemsSource="{Binding Languages}" SelectedItem="{Binding Language}" MinWidth="240" />
</StackPanel>
<ToggleSwitch Content="{DynamicResource general.start_with_system}" IsChecked="{Binding StartWithSystem}" />
<StackPanel>
<TextBlock Classes="label" Text="{DynamicResource general.close_action}" />
<ComboBox ItemsSource="{Binding CloseChoices}" SelectedItem="{Binding CloseChoice}" MinWidth="240" />
</StackPanel>
<ToggleSwitch Content="{Binding ContextMenuText}" IsChecked="{Binding ContextMenu}" IsVisible="{Binding HasSystemIntegration}" />
<TextBlock Classes="caption" TextWrapping="Wrap" Text="{Binding IntegrationMessage}"
IsVisible="{Binding IntegrationMessage, Converter={x:Static StringConverters.IsNotNullOrEmpty}}" />
Expand Down
18 changes: 18 additions & 0 deletions src/Filee.Core/Settings/AppSettings.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// Root settings object persisted to settings.json.

using System.Text.Json.Serialization;
using Filee.Core.Presets;

namespace Filee.Core.Settings;
Expand All @@ -11,6 +12,19 @@ public enum ThemeMode
Dark,
}

/// <summary>What closing the main window does (Settings → General).</summary>
public enum CloseAction
{
/// <summary>Ask, with "Don't ask again" (which saves the answer).</summary>
Ask,

/// <summary>Hide the window; Filee keeps running in the tray.</summary>
Tray,

/// <summary>Quit Filee.</summary>
Quit,
}

/// <summary>Look and feel.</summary>
public sealed class ThemeSettings
{
Expand Down Expand Up @@ -64,6 +78,10 @@ public sealed class AppSettings

public bool CheckForUpdates { get; set; } = true;

/// <summary>What closing the main window does. A value from a newer Filee reads as <see cref="CloseAction.Ask"/>.</summary>
[JsonConverter(typeof(LenientEnumJsonConverter<CloseAction>))]
public CloseAction CloseAction { get; set; } = CloseAction.Ask;

/// <summary>Record finished conversions in the history (home page). Off: nothing new is recorded.</summary>
public bool KeepHistory { get; set; } = true;

Expand Down
Loading
Loading