Compare commits

...

7 Commits

Author SHA1 Message Date
66a92e6838 Update dependency GitInfo to v3.5.0 (#7)
This PR contains the following updates:

| Package | Type | Update | Change |
|---|---|---|---|
| [GitInfo](https://clarius.org/GitInfo) ([source](https://github.com/devlooped/GitInfo)) | nuget | minor | `3.3.5` -> `3.5.0` |

---

### Release Notes

<details>
<summary>devlooped/GitInfo (GitInfo)</summary>

### [`v3.5.0`](https://github.com/devlooped/GitInfo/blob/HEAD/changelog.md#v350-2024-11-02)

[Full Changelog](https://github.com/devlooped/GitInfo/compare/v3.3.5...v3.5.0)

 Implemented enhancements:

-   Question: Why we use git log with format to get the Current commit? [#&#8203;355](https://github.com/devlooped/GitInfo/issues/355)
-   SetGitExe doesn't recognize VS Git locations [#&#8203;324](https://github.com/devlooped/GitInfo/issues/324)
-   Projects don't rebuild after committing [#&#8203;322](https://github.com/devlooped/GitInfo/issues/322)
-   New tag without version drop semver to zeros [#&#8203;319](https://github.com/devlooped/GitInfo/issues/319)
-   Add vswhere fallback to locate git provided by VS [#&#8203;372](https://github.com/devlooped/GitInfo/pull/372) ([@&#8203;kzu](https://github.com/kzu))
-   Simplify IsDirty with newer typed constants in ThisAssembly [#&#8203;368](https://github.com/devlooped/GitInfo/pull/368) ([@&#8203;kzu](https://github.com/kzu))
-   Allow opting-out of GitBranch default in CI [#&#8203;366](https://github.com/devlooped/GitInfo/pull/366) ([@&#8203;kzu](https://github.com/kzu))
-   Set default GitBranch from CI env variables [#&#8203;365](https://github.com/devlooped/GitInfo/pull/365) ([@&#8203;kzu](https://github.com/kzu))
-   Using simpler git command to get current commit [#&#8203;356](https://github.com/devlooped/GitInfo/pull/356) ([@&#8203;kzu](https://github.com/kzu))

🔨 Other:

-   Using GitInfo with Ncrunch [#&#8203;345](https://github.com/devlooped/GitInfo/issues/345)

🔀 Merged:

-   Adopt SL v2 via ThisAssembly.Constants and document in readme(s) [#&#8203;370](https://github.com/devlooped/GitInfo/pull/370) ([@&#8203;kzu](https://github.com/kzu))
-   \+Mᐁ includes [#&#8203;367](https://github.com/devlooped/GitInfo/pull/367) ([@&#8203;devlooped-bot](https://github.com/devlooped-bot))
-   \+Mᐁ includes [#&#8203;357](https://github.com/devlooped/GitInfo/pull/357) ([@&#8203;devlooped-bot](https://github.com/devlooped-bot))
-   Clean code [#&#8203;352](https://github.com/devlooped/GitInfo/pull/352) ([@&#8203;gitfool](https://github.com/gitfool))
-   Remove redundant depends on targets [#&#8203;346](https://github.com/devlooped/GitInfo/pull/346) ([@&#8203;gitfool](https://github.com/gitfool))
-   Fix fast up-to-date-check [#&#8203;343](https://github.com/devlooped/GitInfo/pull/343) ([@&#8203;gitfool](https://github.com/gitfool))
-   Disable rogue command echo [#&#8203;342](https://github.com/devlooped/GitInfo/pull/342) ([@&#8203;gitfool](https://github.com/gitfool))

</details>

---

### Configuration

📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined).

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiIzNy40MjAuMSIsInVwZGF0ZWRJblZlciI6IjM3LjQyMC4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->

Reviewed-on: #7
Co-authored-by: Renovate Bot <renovate-bot@git.cantorgymnasium.de>
Co-committed-by: Renovate Bot <renovate-bot@git.cantorgymnasium.de>
2024-11-03 07:54:06 +01:00
088637ff5d
fix Linux build 2024-09-15 15:18:12 +02:00
23dc2d75e0 add Windows build readme 2024-09-15 15:03:19 +02:00
2a7a7b01cf Configure Renovate (#1)
Welcome to [Renovate](https://github.com/renovatebot/renovate)! This is an onboarding PR to help you understand and configure settings before regular Pull Requests begin.

🚦 To activate Renovate, merge this Pull Request. To disable Renovate, simply close this Pull Request unmerged.

---
### Detected Package Files

 * `go.mod` (gomod)

### What to Expect

With your current configuration, Renovate will create 2 Pull Requests:

<details>
<summary>Update module fyne.io/fyne/v2 to v2.4.5</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/fyne.io-fyne-v2-2.x`
  - Merge into: `main`
  - Upgrade [fyne.io/fyne/v2](https://github.com/fyne-io/fyne) to `v2.4.5`

</details>

<details>
<summary>Update module golang.org/x/image to v0.17.0</summary>

  - Schedule: ["at any time"]
  - Branch name: `renovate/golang.org-x-image-0.x`
  - Merge into: `main`
  - Upgrade golang.org/x/image to `v0.17.0`

</details>

---

 Got questions? Check out Renovate's [Docs](https://docs.renovatebot.com/), particularly the Getting Started section.
If you need any further assistance then you can also [request help here](https://github.com/renovatebot/renovate/discussions).

---

This PR has been generated by [Renovate Bot](https://github.com/renovatebot/renovate).

<!--renovate-config-hash:94693a990c975907e7f13da3309b9d56ba02b3983519b41786edf5cf031e457c-->

Reviewed-on: #1
Co-authored-by: Renovate Bot <renovate-bot@git.cantorgymnasium.de>
Co-committed-by: Renovate Bot <renovate-bot@git.cantorgymnasium.de>
2024-09-15 14:44:15 +02:00
a299ec043b Various fixes for Windows version
- new installer based on InnoSetup
- add MIT license
- produce WinExe output
- other small fixes
2024-09-15 14:38:59 +02:00
a115f37c95 add windows build support 2024-09-14 23:22:12 +02:00
4bd93fa87e fixes and optimizations 2024-09-14 17:49:20 +02:00
14 changed files with 332 additions and 300 deletions

6
.editorconfig Normal file

@ -0,0 +1,6 @@
# CSharp formatting rules:
[*.cs]
csharp_new_line_before_open_brace = none
csharp_new_line_before_else = false
csharp_new_line_before_catch = false
csharp_new_line_before_finally = false

1
.gitignore vendored

@ -3,6 +3,7 @@
project.lock.json project.lock.json
.DS_Store .DS_Store
*.pyc *.pyc
*.exe
nupkg/ nupkg/
# Visual Studio Code # Visual Studio Code

21
LICENSE Normal file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Denys Konovalov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -1,10 +1,11 @@
public class AppInfo namespace Photomator;
{
public static readonly string ApplicationName = "Photomator"; public static class AppInfo {
public static readonly string IconName = "de.cantorgymnasium.Photomator"; public static readonly string ApplicationName = "Photomator";
public static readonly string Version = ThisAssembly.Git.Tag; public static readonly string IconName = "de.cantorgymnasium.Photomator";
public static readonly string ReleaseNotes = @" public static readonly string Version = ThisAssembly.Git.Tag;
<p><em>0.0.1</em></p> public static readonly string ReleaseNotes = @"
<p><em>v0.0.1</em></p>
<p>Initial release</p> <p>Initial release</p>
<ul> <ul>
<li>single-file and folder conversions</li> <li>single-file and folder conversions</li>
@ -12,8 +13,8 @@ public class AppInfo
<li>Windows support</li> <li>Windows support</li>
</ul> </ul>
"; ";
public static readonly string Copyright = "© 2024 Denys Konovalov"; public static readonly string Copyright = "© 2024 Denys Konovalov";
public static readonly string DeveloperName = "Denys Konovalov"; public static readonly string DeveloperName = "Denys Konovalov";
public static readonly string Website = "https://git.cantorgymnasium.de/gcg/Photomator"; public static readonly string Website = "https://git.cantorgymnasium.de/gcg/Photomator";
public static readonly string IssueUrl = "https://git.cantorgymnasium.de/gcg/Photomator/issues"; public static readonly string IssueUrl = "https://git.cantorgymnasium.de/gcg/Photomator/issues";
} }

@ -4,49 +4,35 @@ using SixLabors.ImageSharp.Processing;
namespace Photomator; namespace Photomator;
public class Lib public static class Lib {
{ public static async Task Convert(string src, string dest, ConvertOptions options) {
public static async Task Convert(string src, string dest, ConvertOptions options)
{
Image img = await Image.LoadAsync(src); Image img = await Image.LoadAsync(src);
img.Mutate(i => img.Mutate(i => {
{
i.AutoOrient(); i.AutoOrient();
Size currentSize = i.GetCurrentSize(); Size currentSize = i.GetCurrentSize();
if (currentSize.Height > currentSize.Width) if (currentSize.Height > currentSize.Width) {
{
i.Resize(options.Resolution, 0); i.Resize(options.Resolution, 0);
} } else {
else
{
i.Resize(0, options.Resolution); i.Resize(0, options.Resolution);
} }
}); });
await img.SaveAsWebpAsync(dest, new WebpEncoder await img.SaveAsWebpAsync(dest, new WebpEncoder {
{
FileFormat = options.Format FileFormat = options.Format
}); });
} }
public static async Task<ConvertError[]> ConvertList(string[] src, string dest, ConvertOptions options, Action<double> setProgress) public static async Task<ConvertError[]> ConvertList(string[] src, string dest, ConvertOptions options, Action<double> setProgress) {
{
double progress = 0; double progress = 0;
List<ConvertError> unconverted = []; List<ConvertError> unconverted = [];
await Parallel.ForEachAsync(src, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, async (path, _) => await Parallel.ForEachAsync(src, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount }, async (path, _) => {
{ string filename = path.Split(Path.DirectorySeparatorChar)[^1];
string filename = path.Split(Path.DirectorySeparatorChar).Last();
string[] splitted = filename.Split('.'); string[] splitted = filename.Split('.');
splitted[^1] = "webp"; splitted[^1] = "webp";
try try {
{
await Convert(path, dest + Path.DirectorySeparatorChar + string.Join(".", splitted), options); await Convert(path, dest + Path.DirectorySeparatorChar + string.Join(".", splitted), options);
} } catch (UnknownImageFormatException) {
catch (UnknownImageFormatException)
{
unconverted.Add(new ConvertError(filename, "Das Dateiformat wird nicht unterstützt.")); unconverted.Add(new ConvertError(filename, "Das Dateiformat wird nicht unterstützt."));
} } catch (Exception e) {
catch (Exception e)
{
unconverted.Add(new ConvertError(filename, e.Message)); unconverted.Add(new ConvertError(filename, e.Message));
} }
progress += 1.0 / src.Length; progress += 1.0 / src.Length;
@ -56,14 +42,12 @@ public class Lib
} }
} }
public readonly struct ConvertOptions(string? resolution, uint format) public readonly struct ConvertOptions(string? resolution, uint format) {
{
public int Resolution { get; init; } = resolution != null ? Convert.ToInt32(resolution) : 1440; public int Resolution { get; init; } = resolution != null ? Convert.ToInt32(resolution) : 1440;
public WebpFileFormatType Format { get; init; } = format == 1 ? WebpFileFormatType.Lossless : WebpFileFormatType.Lossy; public WebpFileFormatType Format { get; init; } = format == 1 ? WebpFileFormatType.Lossless : WebpFileFormatType.Lossy;
} }
public readonly struct ConvertError(string file, string message) public readonly struct ConvertError(string file, string message) {
{
public string File { get; init; } = file; public string File { get; init; } = file;
public string Message { get; init; } = message; public string Message { get; init; } = message;
} }

@ -1,15 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>WinExe</OutputType>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<SelfContained>true</SelfContained>
<ApplicationIcon>Resources\de.cantorgymnasium.Photomator.ico</ApplicationIcon>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="GirCore.Adw-1" Version="0.5.0" /> <PackageReference Include="GirCore.Adw-1" Version="0.5.0" />
<PackageReference Include="GitInfo" Version="3.3.5"> <PackageReference Include="GitInfo" Version="3.5.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
</PackageReference> </PackageReference>
@ -17,8 +19,33 @@
</ItemGroup> </ItemGroup>
<Target Name="PreBuild" BeforeTargets="PreBuildEvent"> <Target Name="PreBuild" BeforeTargets="PreBuildEvent">
<Exec Command="echo Compiling extra resources..." /> <Message Text="=== Building Photomator $(GitTag)... ===" Importance="high" />
<RemoveDir Directories="$(PublishDir)" />
<Message Text="=== Compiling extra resources... ===" Importance="high" />
<Exec Command="glib-compile-resources --sourcedir ./Resources ./Resources/de.cantorgymnasium.Photomator.gresource.xml --target=$(OutDir)/de.cantorgymnasium.Photomator.gresource" /> <Exec Command="glib-compile-resources --sourcedir ./Resources ./Resources/de.cantorgymnasium.Photomator.gresource.xml --target=$(OutDir)/de.cantorgymnasium.Photomator.gresource" />
</Target> </Target>
<Target Name="CopyFiles" AfterTargets="Publish">
<ItemGroup>
<SourceFiles Include="$(PublishDir)\**\*.*" />
</ItemGroup>
<Move SourceFiles="@(SourceFiles)" DestinationFolder="$(PublishDir)\bin" />
<Copy SourceFiles="$(OutDir)/de.cantorgymnasium.Photomator.gresource" DestinationFolder="$(PublishDir)\share\de.cantorgymnasium.Photomator" />
</Target>
<Target Name="CopyFilesWindows" AfterTargets="Publish" Condition="'$(RuntimeIdentifier)' == 'win-x64'">
<Message Text="=== Copying Gtk files for win-x64 runtime ... ===" Importance="high"/>
<ItemGroup>
<GtkDlls Include="libgtk-4-1.dll;libadwaita-1-0.dll;libappstream-5.dll;libbrotlicommon.dll;libbrotlidec.dll;libbz2-1.dll;libcairo-2.dll;libcairo-gobject-2.dll;libcairo-script-interpreter-2.dll;libcrypto-3-x64.dll;libcurl-4.dll;libdatrie-1.dll;libdeflate.dll;libepoxy-0.dll;libexpat-1.dll;libffi-8.dll;libfontconfig-1.dll;libfreetype-6.dll;libfribidi-0.dll;libgcc_s_seh-1.dll;libgdk_pixbuf-2.0-0.dll;libgio-2.0-0.dll;libglib-2.0-0.dll;libgmodule-2.0-0.dll;libgobject-2.0-0.dll;libgraphene-1.0-0.dll;libgraphite2.dll;libharfbuzz-0.dll;libharfbuzz-gobject-0.dll;libiconv-2.dll;libidn2-0.dll;libintl-8.dll;libjbig-0.dll;libjpeg-8.dll;libLerc.dll;liblzma-5.dll;liblzo2-2.dll;libnghttp2-14.dll;libnghttp3-9.dll;libpango-1.0-0.dll;libpangocairo-1.0-0.dll;libpangoft2-1.0-0.dll;libpangowin32-1.0-0.dll;libpcre2-8-0.dll;libpixman-1-0.dll;libpng16-16.dll;libpsl-5.dll;librsvg-2-2.dll;libsharpyuv-0.dll;libssh2-1.dll;libssl-3-x64.dll;libstdc++-6.dll;libthai-0.dll;libtiff-6.dll;libunistring-5.dll;libwebp-7.dll;libwinpthread-1.dll;libxml2-2.dll;libxmlb-2.dll;libyaml-0-2.dll;libzstd.dll;zlib1.dll;gdbus.exe"/>
<Icons Include="C:\msys64\mingw64\share\icons\**\*.*" />
</ItemGroup>
<Copy SourceFiles="@(GtkDlls->'C:\msys64\mingw64\bin\%(Filename)%(Extension)')" DestinationFolder="$(PublishDir)\bin" />
<Copy SourceFiles="C:\msys64\mingw64\lib\gdk-pixbuf-2.0\2.10.0\loaders\libpixbufloader-svg.dll" DestinationFolder="$(PublishDir)\lib\gdk-pixbuf-2.0\2.10.0\loaders" />
<Copy SourceFiles=".\Resources\loaders.cache" DestinationFolder="$(PublishDir)\lib\gdk-pixbuf-2.0\2.10.0" />
<Copy SourceFiles="C:\msys64\mingw64\share\glib-2.0\schemas\gschemas.compiled" DestinationFolder="$(PublishDir)\share\glib-2.0\schemas" />
<Copy SourceFiles="@(Icons)" DestinationFolder="$(PublishDir)\share\icons\%(RecursiveDir)" />
<Message Text="=== Building Windows installer ... ===" Importance="high"/>
<Exec Command='"C:\Program Files (x86)\Inno Setup 6\ISCC.exe" Resources\Photomator.iss -DMyAppVersion=$(GitTag)' />
</Target>
</Project> </Project>

@ -3,37 +3,28 @@ using Photomator.Views;
namespace Photomator; namespace Photomator;
public partial class Program public partial class Program {
{
public delegate void OpenCallback(nint application, nint[] files, int n_files, nint hint, nint data); public delegate void OpenCallback(nint application, nint[] files, int n_files, nint hint, nint data);
private readonly Adw.Application _application; private readonly Adw.Application _application;
public static int Main() => new Program().Run(); public static int Main() => new Program().Run();
public Program() public Program() {
{
_application = Adw.Application.New("de.cantorgymnasium.Photomator", Gio.ApplicationFlags.FlagsNone); _application = Adw.Application.New("de.cantorgymnasium.Photomator", Gio.ApplicationFlags.FlagsNone);
Gtk.Window.SetDefaultIconName("de.cantorgymnasium.Photomator"); Gtk.Window.SetDefaultIconName("de.cantorgymnasium.Photomator");
if (File.Exists(Path.GetFullPath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!) + "/de.cantorgymnasium.Photomator.gresource")) string localPath = Path.GetFullPath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!) + "/de.cantorgymnasium.Photomator.gresource";
{ if (File.Exists(localPath))
Gio.Functions.ResourcesRegister(Gio.Functions.ResourceLoad(Path.GetFullPath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!) + "/de.cantorgymnasium.Photomator.gresource")); Gio.Functions.ResourcesRegister(Gio.Functions.ResourceLoad(localPath));
} else {
else
{
var prefixes = new List<string> { var prefixes = new List<string> {
Directory.GetParent(Directory.GetParent(Path.GetFullPath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!))!.FullName)!.FullName, Directory.GetParent(Directory.GetParent(Path.GetFullPath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!))!.FullName)!.FullName,
Directory.GetParent(Path.GetFullPath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!))!.FullName, Directory.GetParent(Path.GetFullPath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)!))!.FullName,
"/usr" "/usr"
}; };
foreach (var prefix in prefixes) string? prefix = prefixes.Find(prefix => File.Exists(prefix + "/share/de.cantorgymnasium.Photomator/de.cantorgymnasium.Photomator.gresource"));
{ if (prefix != null)
if (File.Exists(prefix + "/share/de.cantorgymnasium.Photomator/de.cantorgymnasium.Photomator.gresource")) Gio.Functions.ResourcesRegister(Gio.Functions.ResourceLoad(Path.GetFullPath(prefix + "/share/de.cantorgymnasium.Photomator/de.cantorgymnasium.Photomator.gresource")));
{
Gio.Functions.ResourcesRegister(Gio.Functions.ResourceLoad(Path.GetFullPath(prefix + "/share/de.cantorgymnasium.Photomator/de.cantorgymnasium.Photomator.gresource")));
break;
}
}
} }
_application.OnActivate += (_, _) => new MainWindow(_application).Start(); _application.OnActivate += (_, _) => new MainWindow(_application).Start();
} }

@ -0,0 +1,65 @@
; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
#define MyAppName "Photomator"
#ifndef MyAppVersion
#define MyAppVersion "v0.0.0-dev"
#endif
#define MyAppPublisher "Georg-Cantor-Gymnasium Halle (Saale)"
#define MyAppURL "https://git.cantorgymnasium.de/gcg/photomator/"
#define MyAppSupportURL "https://git.cantorgymnasium.de/gcg/photomator/issues/"
#define MyAppUpdatesURL "https://git.cantorgymnasium.de/gcg/photomator/releases/"
#define MyAppExeName "Photomator.exe"
[Setup]
; NOTE: The value of AppId uniquely identifies this application. Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{2F93EA62-DD17-4ACF-87B9-4B80B5217CC4}
AppName={#MyAppName}
;AppVersion={#MyAppVersion}
AppVerName={#MyAppName} {#MyAppVersion}
AppPublisher={#MyAppPublisher}
AppPublisherURL={#MyAppURL}
AppSupportURL={#MyAppSupportURL}
AppUpdatesURL={#MyAppUpdatesURL}
DefaultDirName={autopf}\{#MyAppName}
; "ArchitecturesAllowed=x64compatible" specifies that Setup cannot run
; on anything but x64 and Windows 11 on Arm.
ArchitecturesAllowed=x64compatible
; "ArchitecturesInstallIn64BitMode=x64compatible" requests that the
; install be done in "64-bit mode" on x64 or Windows 11 on Arm,
; meaning it should use the native 64-bit Program Files directory and
; the 64-bit view of the registry.
ArchitecturesInstallIn64BitMode=x64compatible
DisableProgramGroupPage=yes
LicenseFile=..\..\LICENSE
; Uncomment the following line to run in non administrative install mode (install for current user only.)
;PrivilegesRequired=lowest
OutputDir=..\bin\Release\net8.0\win-x64\publish
OutputBaseFilename={#MyAppName}-{#MyAppVersion}-win-x64-setup
SetupIconFile=de.cantorgymnasium.Photomator.ico
Compression=lzma
SolidCompression=yes
WizardStyle=modern
[Languages]
Name: "german"; MessagesFile: "compiler:Languages\German.isl"
[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
[Files]
Source: "..\bin\Release\net8.0\win-x64\publish\bin\*"; DestDir: "{app}\bin"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "..\bin\Release\net8.0\win-x64\publish\lib\*"; DestDir: "{app}\lib"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "..\bin\Release\net8.0\win-x64\publish\share\*"; DestDir: "{app}\share"; Flags: ignoreversion recursesubdirs createallsubdirs
; NOTE: Don't use "Flags: ignoreversion" on any shared system files
[Icons]
Name: "{autoprograms}\{#MyAppName}"; Filename: "{app}\bin\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\bin\{#MyAppExeName}"; Tasks: desktopicon
[Run]
Filename: "{app}\bin\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

@ -0,0 +1,7 @@
"lib\\gdk-pixbuf-2.0\\2.10.0\\loaders\\libpixbufloader-svg.dll"
"svg" 6 "gdk-pixbuf" "Scalable Vector Graphics" "LGPL"
"image/svg+xml" "image/svg" "image/svg-xml" "image/vnd.adobe.svg+xml" "text/xml-svg" "image/svg+xml-compressed" ""
"svg" "svgz" "svg.gz" ""
" <svg" "* " 100
" <!DOCTYPE svg" "* " 100

@ -1,32 +1,25 @@
using Gtk; using Gtk;
using Adw; using Adw;
using System.Text; using System.Text;
using GLib;
namespace Photomator.Views; namespace Photomator.Views;
public partial class MainWindow : Adw.ApplicationWindow public partial class MainWindow : Adw.ApplicationWindow {
{
private readonly Adw.Application _application; private readonly Adw.Application _application;
public MainWindow(Adw.Application application) : base() public MainWindow(Adw.Application application) : base() {
{
_application = application; _application = application;
SetTitle(AppInfo.ApplicationName); SetTitle(AppInfo.ApplicationName);
SetIconName(AppInfo.IconName); SetIconName(AppInfo.IconName);
PreferencesPage layoutSingle = InitPageSingle(); PreferencesPage pageSingle = InitPageSingle();
PreferencesPage layoutFolder = InitPageFolder(); PreferencesPage pageFolder = InitPageFolder();
ViewStack viewStack = ViewStack.New(); ViewStack viewStack = ViewStack.New();
viewStack.Add(layoutSingle); viewStack.AddTitledWithIcon(pageSingle, "page-single", "Einzelne Datei", "image-x-generic-symbolic");
ViewStackPage pageSingle = viewStack.GetPage(layoutSingle); viewStack.AddTitledWithIcon(pageFolder, "page-folder", "Ordner", "folder-symbolic");
pageSingle.SetTitle("Einzelne Datei");
pageSingle.SetIconName("image-x-generic-symbolic");
viewStack.Add(layoutFolder);
ViewStackPage pageFolder = viewStack.GetPage(layoutFolder);
pageFolder.SetTitle("Ordner");
pageFolder.SetIconName("folder-symbolic");
ViewSwitcher viewSwitcher = ViewSwitcher.New(); ViewSwitcher viewSwitcher = ViewSwitcher.New();
viewSwitcher.SetStack(viewStack); viewSwitcher.SetStack(viewStack);
@ -50,22 +43,20 @@ public partial class MainWindow : Adw.ApplicationWindow
SetContent(view); SetContent(view);
} }
public void Start() public void Start() {
{
_application.AddWindow(this); _application.AddWindow(this);
Present(); Present();
} }
public PreferencesPage InitPageSingle() public PreferencesPage InitPageSingle() {
{
PreferencesPage page = PreferencesPage.New(); PreferencesPage page = PreferencesPage.New();
PreferencesGroup pgSelect = PreferencesGroup.New(); PreferencesGroup pgSource = PreferencesGroup.New();
pgSelect.SetTitle("Bild"); pgSource.SetTitle("Bild");
pgSelect.SetDescription("Als Eingabeformate werden BMP, GIF, JPEG, PBM, PNG, TIFF, TGA und WebP unterstützt."); pgSource.SetDescription("Als Eingabeformate werden BMP, GIF, JPEG, PBM, PNG, TIFF, TGA und WebP unterstützt.");
EntryRow erSelect = EntryRow.New(); EntryRow erSource = EntryRow.New();
erSelect.SetTitle("Zu konvertierendes Bild"); erSource.SetTitle("Zu konvertierendes Bild");
erSelect.SetEditable(false); erSource.SetEditable(false);
PreferencesGroup pgOptions = PreferencesGroup.New(); PreferencesGroup pgOptions = PreferencesGroup.New();
pgOptions.SetTitle("Optionen"); pgOptions.SetTitle("Optionen");
@ -89,8 +80,7 @@ public partial class MainWindow : Adw.ApplicationWindow
pgBtn.SetValign(Align.Center); pgBtn.SetValign(Align.Center);
pgBtn.SetHalign(Align.Center); pgBtn.SetHalign(Align.Center);
Button btnConvert = Button.NewWithLabel("Konvertieren"); Button btnConvert = Button.NewWithLabel("Konvertieren");
btnConvert.OnClicked += async (_, _) => btnConvert.OnClicked += async (_, _) => {
{
FileDialog fileDialog = FileDialog.New(); FileDialog fileDialog = FileDialog.New();
Gio.ListStore listStore = Gio.ListStore.New(FileFilter.GetGType()); Gio.ListStore listStore = Gio.ListStore.New(FileFilter.GetGType());
FileFilter filter = FileFilter.New(); FileFilter filter = FileFilter.New();
@ -100,21 +90,16 @@ public partial class MainWindow : Adw.ApplicationWindow
listStore.Append(filter); listStore.Append(filter);
fileDialog.SetFilters(listStore); fileDialog.SetFilters(listStore);
Gio.File? file; Gio.File? file;
try try {
{
file = await fileDialog.SaveAsync(this); file = await fileDialog.SaveAsync(this);
} } catch {
catch
{
file = null; file = null;
} }
if (file != null && file.GetPath() != null) if (file != null && file.GetPath() != null) {
{
ConvertOptions options = new(resOptions.GetString(crRes.GetSelected()), crFormat.GetSelected()); ConvertOptions options = new(resOptions.GetString(crRes.GetSelected()), crFormat.GetSelected());
await Lib.Convert(erSelect.GetText(), file.GetPath()!, options); await Lib.Convert(erSource.GetText(), file.GetPath()!, options);
} } else
else
InitInfoAlert("Kein Ziel ausgewählt", "Es wurde keine Zieldatei ausgewählt. Konvertierung wird abgebrochen."); InitInfoAlert("Kein Ziel ausgewählt", "Es wurde keine Zieldatei ausgewählt. Konvertierung wird abgebrochen.");
}; };
btnConvert.SetCssClasses(["pill", "suggested-action", "long"]); btnConvert.SetCssClasses(["pill", "suggested-action", "long"]);
@ -125,63 +110,33 @@ public partial class MainWindow : Adw.ApplicationWindow
btnSelect.SetValign(Align.Center); btnSelect.SetValign(Align.Center);
btnSelect.AddCssClass("flat"); btnSelect.AddCssClass("flat");
btnSelect.SetIconName("document-open-symbolic"); btnSelect.SetIconName("document-open-symbolic");
btnSelect.OnClicked += async (_, _) => btnSelect.OnClicked += async (_, _) => {
{
FileDialog fileDialog = FileDialog.New(); FileDialog fileDialog = FileDialog.New();
Gio.ListStore listStore = Gio.ListStore.New(FileFilter.GetGType()); fileDialog.SetFilters(GetSourceFileFilter());
FileFilter filterAll = FileFilter.New();
filterAll.SetName("Alle unterstützten Bildformate");
string[][] formats = [["bmp", "dib"], ["gif"], ["jpeg", "jpg", "jpe", "jfif"], ["pbm"], ["png"], ["tiff", "tif"], ["tga", "bpx", "icb", "pix"], ["webp"]];
foreach (string[] format in formats)
{
FileFilter filter = FileFilter.New();
StringBuilder name = new($"{format[0].ToUpper()} (");
for (int i = 0; i < format.Length; i++)
{
name.Append($"*.{format[i]}");
if (i != format.Length - 1)
name.Append(", ");
filter.AddPattern($"*.{format[i]}");
filterAll.AddPattern($"*.{format[i]}");
filter.AddPattern($"*.{format[i].ToUpper()}");
filterAll.AddPattern($"*.{format[i].ToUpper()}");
}
name.Append(')');
filter.SetName(name.ToString());
listStore.Append(filter);
}
listStore.Insert(0, filterAll);
fileDialog.SetFilters(listStore);
Gio.File? file; Gio.File? file;
try try {
{
file = await fileDialog.OpenAsync(this); file = await fileDialog.OpenAsync(this);
} } catch {
catch
{
file = null; file = null;
} }
if (file != null && file.GetPath() != null) if (file != null && file.GetPath() != null) {
{ erSource.SetText(file.GetPath()!);
erSelect.SetText(file.GetPath()!);
btnConvert.SetSensitive(true); btnConvert.SetSensitive(true);
} } else if (erSource.GetText() == "")
else if (erSelect.GetText() == "")
btnConvert.SetSensitive(false); btnConvert.SetSensitive(false);
}; };
erSelect.AddSuffix(btnSelect); erSource.AddSuffix(btnSelect);
pgSelect.Add(erSelect); pgSource.Add(erSource);
page.Add(pgSelect); page.Add(pgSource);
page.Add(pgOptions); page.Add(pgOptions);
page.Add(pgBtn); page.Add(pgBtn);
return page; return page;
} }
public PreferencesPage InitPageFolder() public PreferencesPage InitPageFolder() {
{
EntryRow erSource = EntryRow.New(); EntryRow erSource = EntryRow.New();
erSource.SetTitle("Quellordner"); erSource.SetTitle("Quellordner");
erSource.SetEditable(false); erSource.SetEditable(false);
@ -212,96 +167,44 @@ public partial class MainWindow : Adw.ApplicationWindow
pgBtn.SetValign(Align.Center); pgBtn.SetValign(Align.Center);
pgBtn.SetHalign(Align.Center); pgBtn.SetHalign(Align.Center);
Button btnConvert = Button.NewWithLabel("Konvertieren"); Button btnConvert = Button.NewWithLabel("Konvertieren");
btnConvert.OnClicked += async (_, _) => btnConvert.OnClicked += async (_, _) => {
{ string[] filesSource;
if (erSource.GetText() != "" && erDest.GetText() != "") if (Directory.Exists(erSource.GetText()))
{ filesSource = Directory.GetFiles(erSource.GetText());
string[] filesSource; else {
if (Directory.Exists(erSource.GetText())) InitInfoAlert("Quellordner existiert nicht", "Der ausgewählte Quellordner ist nicht im Dateisystem vorhanden. Die Konvertierung wurde abgebrochen.");
filesSource = Directory.GetFiles(erSource.GetText()); return;
}
if (filesSource.Length == 0) {
InitInfoAlert("Quellordner ist leer", "Im ausgewählten Quellordner befinden sich keine Dateien. Die Konvertierung wurde abgebrochen.");
return;
}
string dest = erDest.GetText();
Directory.CreateDirectory(dest);
StatusDialog statusDialog = new(_application, this);
statusDialog.Start();
try {
ConvertOptions options = new(resOptions.GetString(crRes.GetSelected()), crFormat.GetSelected());
ConvertError[] convertErrors = await Lib.ConvertList(filesSource, dest, options, statusDialog.SetProgress);
if (convertErrors.Length > 0)
statusDialog.Partial(filesSource.Length, dest, convertErrors);
else else
{ statusDialog.Success(filesSource.Length, dest);
InitInfoAlert("Quellordner existiert nicht", "Der ausgewählte Quellordner ist nicht im Dateisystem vorhanden. Die Konvertierung wurde abgebrochen."); } catch (Exception e) {
return; statusDialog.Error(e.Message);
}
if (filesSource.Length == 0)
{
InitInfoAlert("Quellordner ist leer", "Im ausgewählten Quellordner befinden sich keine Dateien. Die Konvertierung wurde abgebrochen.");
return;
}
string dest = erDest.GetText();
if (!Directory.Exists(dest))
Directory.CreateDirectory(dest);
StatusDialog statusDialog = new(_application, this);
statusDialog.Start();
try
{
ConvertOptions options = new ConvertOptions(resOptions.GetString(crRes.GetSelected()), crFormat.GetSelected());
ConvertError[] convertErrors = await Lib.ConvertList(filesSource, dest, options, statusDialog.SetProgress);
if (convertErrors.Length > 0)
statusDialog.Partial(filesSource.Length, dest, convertErrors);
else
statusDialog.Success(filesSource.Length, dest);
}
catch (Exception e)
{
statusDialog.Error(e.Message);
}
} }
}; };
btnConvert.SetCssClasses(["pill", "suggested-action", "long"]); btnConvert.SetCssClasses(["pill", "suggested-action", "long"]);
btnConvert.SetSensitive(false); btnConvert.SetSensitive(false);
pgBtn.Add(btnConvert); pgBtn.Add(btnConvert);
void checkButtonState() void checkButtonState() {
{
if (erSource.GetText() != "" && erDest.GetText() != "") btnConvert.SetSensitive(true); if (erSource.GetText() != "" && erDest.GetText() != "") btnConvert.SetSensitive(true);
else btnConvert.SetSensitive(false); else btnConvert.SetSensitive(false);
} }
Button btnSource = Button.New(); erSource.AddSuffix(InitButtonSelectFolder(erSource, checkButtonState));
btnSource.SetValign(Align.Center); erDest.AddSuffix(InitButtonSelectFolder(erDest, checkButtonState));
btnSource.AddCssClass("flat");
btnSource.SetIconName("document-open-symbolic");
btnSource.OnClicked += async (_, _) =>
{
FileDialog fileDialog = FileDialog.New();
Gio.File? file;
try
{
file = await fileDialog.SelectFolderAsync(this);
}
catch
{
file = null;
}
if (file != null && file.GetPath() != null)
erSource.SetText(file.GetPath()!);
checkButtonState();
};
erSource.AddSuffix(btnSource);
Button btnDest = Button.New();
btnDest.SetValign(Align.Center);
btnDest.AddCssClass("flat");
btnDest.SetIconName("document-open-symbolic");
btnDest.OnClicked += async (_, _) =>
{
FileDialog fileDialog = FileDialog.New();
Gio.File? file;
try
{
file = await fileDialog.SelectFolderAsync(this);
}
catch
{
file = null;
}
if (file != null && file.GetPath() != null)
erDest.SetText(file.GetPath()!);
checkButtonState();
};
erDest.AddSuffix(btnDest);
PreferencesPage page = PreferencesPage.New(); PreferencesPage page = PreferencesPage.New();
@ -317,8 +220,7 @@ public partial class MainWindow : Adw.ApplicationWindow
return page; return page;
} }
public void InitInfoAlert(string title, string body) public void InitInfoAlert(string title, string body) {
{
Adw.AlertDialog alert = Adw.AlertDialog.New(title, body); Adw.AlertDialog alert = Adw.AlertDialog.New(title, body);
alert.AddResponse("ok", "OK"); alert.AddResponse("ok", "OK");
alert.SetResponseAppearance("ok", ResponseAppearance.Suggested); alert.SetResponseAppearance("ok", ResponseAppearance.Suggested);
@ -327,37 +229,7 @@ public partial class MainWindow : Adw.ApplicationWindow
alert.Present(this); alert.Present(this);
} }
public bool InitDestructiveAlert(string title, string body, string resp) public static Adw.AboutDialog InitAboutDialog() {
{
Adw.AlertDialog alert = Adw.AlertDialog.New(title, body);
alert.AddResponse("cancel", "Abbrechen");
alert.AddResponse(resp.ToLower(), resp);
alert.SetResponseAppearance(resp.ToLower(), ResponseAppearance.Destructive);
alert.SetDefaultResponse("cancel");
alert.SetCloseResponse("cancel");
bool running = true;
bool response = false;
alert.OnResponse += (Adw.AlertDialog _, Adw.AlertDialog.ResponseSignalArgs args) =>
{
if (args.Response.Equals(resp, StringComparison.CurrentCultureIgnoreCase))
{
response = true;
}
running = false;
};
alert.Present(this);
while (running == true)
{
}
return response;
}
public Adw.AboutDialog InitAboutDialog()
{
Adw.AboutDialog aboutDialog = Adw.AboutDialog.New(); Adw.AboutDialog aboutDialog = Adw.AboutDialog.New();
aboutDialog.SetApplicationName(AppInfo.ApplicationName); aboutDialog.SetApplicationName(AppInfo.ApplicationName);
aboutDialog.SetDeveloperName(AppInfo.DeveloperName); aboutDialog.SetDeveloperName(AppInfo.DeveloperName);
@ -371,4 +243,51 @@ public partial class MainWindow : Adw.ApplicationWindow
return aboutDialog; return aboutDialog;
} }
public Button InitButtonSelectFolder(EntryRow entryRow, VoidFunc checkButtonState) {
Button btnSelect = Button.New();
btnSelect.SetValign(Align.Center);
btnSelect.AddCssClass("flat");
btnSelect.SetIconName("document-open-symbolic");
btnSelect.OnClicked += async (_, _) => {
FileDialog fileDialog = FileDialog.New();
Gio.File? file;
try {
file = await fileDialog.SelectFolderAsync(this);
} catch {
file = null;
}
if (file != null && file.GetPath() != null)
entryRow.SetText(file.GetPath()!);
checkButtonState();
};
return btnSelect;
}
public static Gio.ListStore GetSourceFileFilter() {
Gio.ListStore listStore = Gio.ListStore.New(FileFilter.GetGType());
FileFilter filterAll = FileFilter.New();
filterAll.SetName("Alle unterstützten Bildformate");
string[][] formats = [["bmp", "dib"], ["gif"], ["jpeg", "jpg", "jpe", "jfif"], ["pbm"], ["png"], ["tiff", "tif"], ["tga", "bpx", "icb", "pix"], ["webp"]];
foreach (string[] format in formats) {
FileFilter filter = FileFilter.New();
StringBuilder name = new($"{format[0].ToUpper()} (");
for (int i = 0; i < format.Length; i++) {
name.Append($"*.{format[i]}");
if (i != format.Length - 1)
name.Append(", ");
filter.AddPattern($"*.{format[i]}");
filterAll.AddPattern($"*.{format[i]}");
filter.AddPattern($"*.{format[i].ToUpper()}");
filterAll.AddPattern($"*.{format[i].ToUpper()}");
}
name.Append(')');
filter.SetName(name.ToString());
listStore.Append(filter);
}
listStore.Insert(0, filterAll);
return listStore;
}
} }

@ -1,18 +1,17 @@
using Gtk; using Gtk;
using Adw; using Adw;
using System.Text; using System.Text;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace Photomator.Views; namespace Photomator.Views;
public partial class StatusDialog : Adw.Window public partial class StatusDialog : Adw.Window {
{
private readonly Adw.Application _application; private readonly Adw.Application _application;
private readonly Adw.ApplicationWindow _mainWindow; private readonly Adw.ApplicationWindow _mainWindow;
private readonly ViewStack _stack;
private readonly ProgressBar _progress; private readonly ProgressBar _progress;
public StatusDialog(Adw.Application application, Adw.ApplicationWindow parent) : base() public StatusDialog(Adw.Application application, Adw.ApplicationWindow parent) : base() {
{
_application = application; _application = application;
_mainWindow = parent; _mainWindow = parent;
_progress = InitProgressBar(); _progress = InitProgressBar();
@ -22,23 +21,16 @@ public partial class StatusDialog : Adw.Window
SetDeletable(true); SetDeletable(true);
SetDefaultSize(400, 500); SetDefaultSize(400, 500);
_stack = ViewStack.New();
_stack.SetVexpand(true);
StatusPage pageStatus = InitPageProgress(); StatusPage pageStatus = InitPageProgress();
_stack.AddNamed(pageStatus, "page-status"); SetContent(pageStatus);
SetContent(_stack);
} }
public void Start() public void Start() {
{
_application.AddWindow(this); _application.AddWindow(this);
Present(); Present();
} }
private StatusPage InitPageProgress() private StatusPage InitPageProgress() {
{
StatusPage pageStatus = StatusPage.New(); StatusPage pageStatus = StatusPage.New();
pageStatus.SetTitle("Konvertierung wird ausgeführt..."); pageStatus.SetTitle("Konvertierung wird ausgeführt...");
pageStatus.SetDescription("Das kann eine Weile dauern."); pageStatus.SetDescription("Das kann eine Weile dauern.");
@ -47,8 +39,7 @@ public partial class StatusDialog : Adw.Window
return pageStatus; return pageStatus;
} }
private ProgressBar InitProgressBar() private static ProgressBar InitProgressBar() {
{
ProgressBar progressBar = ProgressBar.New(); ProgressBar progressBar = ProgressBar.New();
progressBar.WidthRequest = 300; progressBar.WidthRequest = 300;
progressBar.SetHalign(Align.Center); progressBar.SetHalign(Align.Center);
@ -62,24 +53,26 @@ public partial class StatusDialog : Adw.Window
return progressBar; return progressBar;
} }
public void SetProgress(double progress) public void SetProgress(double progress) {
{
_progress.SetFraction(progress); _progress.SetFraction(progress);
_progress.SetText(string.Format("{0:p0}", progress)); _progress.SetText(string.Format("{0:p0}", progress));
} }
private StatusPage InitPageSuccess(int number, string dest) private StatusPage InitPageSuccess(int number, string dest) {
{
StatusPage pageStatus = StatusPage.New(); StatusPage pageStatus = StatusPage.New();
pageStatus.SetTitle("Die Konvertierung wurde erfolgreich abgeschlossen"); pageStatus.SetTitle("Die Konvertierung wurde erfolgreich abgeschlossen");
pageStatus.SetDescription($"{number} Dateien wurden erfolgreich konvertiert."); pageStatus.SetDescription($"{number} Dateien wurden erfolgreich konvertiert.");
pageStatus.SetIconName("selection-mode-symbolic"); pageStatus.SetIconName("selection-mode-symbolic");
Button btnOpen = Button.NewWithLabel("Ordner öffnen"); Button btnOpen = Button.NewWithLabel("Ordner öffnen");
btnOpen.OnClicked += async (_, _) => btnOpen.OnClicked += async (_, _) => {
{ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
FileLauncher fileLauncher = FileLauncher.New(Gio.FileHelper.NewForPath(dest)); FileLauncher fileLauncher = FileLauncher.New(Gio.FileHelper.NewForPath(dest));
await fileLauncher.LaunchAsync(_mainWindow); await fileLauncher.LaunchAsync(_mainWindow);
} else {
Process.Start("explorer.exe", @$"{dest}");
}
Close(); Close();
}; };
btnOpen.SetCssClasses(["pill", "suggested-action"]); btnOpen.SetCssClasses(["pill", "suggested-action"]);
@ -90,15 +83,12 @@ public partial class StatusDialog : Adw.Window
return pageStatus; return pageStatus;
} }
public void Success(int number, string dest) public void Success(int number, string dest) {
{
StatusPage pageSuccess = InitPageSuccess(number, dest); StatusPage pageSuccess = InitPageSuccess(number, dest);
_stack.AddNamed(pageSuccess, "page-success"); SetContent(pageSuccess);
_stack.SetVisibleChildName("page-success");
} }
private StatusPage InitPagePartial(int number, string dest, ConvertError[] convertErrors) private StatusPage InitPagePartial(int number, string dest, ConvertError[] convertErrors) {
{
StatusPage pageStatus = StatusPage.New(); StatusPage pageStatus = StatusPage.New();
pageStatus.SetTitle("Bei der Konvertierung sind Fehler aufgetreten"); pageStatus.SetTitle("Bei der Konvertierung sind Fehler aufgetreten");
pageStatus.SetDescription($"{number - convertErrors.Length} von {number} Dateien wurden erfolgreich konvertiert."); pageStatus.SetDescription($"{number - convertErrors.Length} von {number} Dateien wurden erfolgreich konvertiert.");
@ -107,13 +97,16 @@ public partial class StatusDialog : Adw.Window
Box box = Box.New(Orientation.Vertical, 36); Box box = Box.New(Orientation.Vertical, 36);
box.SetHalign(Align.Center); box.SetHalign(Align.Center);
if (convertErrors.Length < number) if (convertErrors.Length < number) {
{
Button btnOpen = Button.NewWithLabel("Ordner öffnen"); Button btnOpen = Button.NewWithLabel("Ordner öffnen");
btnOpen.OnClicked += async (_, _) => btnOpen.OnClicked += async (_, _) => {
{ if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
FileLauncher fileLauncher = FileLauncher.New(Gio.FileHelper.NewForPath(dest)); FileLauncher fileLauncher = FileLauncher.New(Gio.FileHelper.NewForPath(dest));
await fileLauncher.LaunchAsync(_mainWindow); await fileLauncher.LaunchAsync(_mainWindow);
} else {
Process.Start("explorer.exe", @$"{dest}");
}
Close(); Close();
}; };
btnOpen.SetCssClasses(["pill", "suggested-action"]); btnOpen.SetCssClasses(["pill", "suggested-action"]);
@ -128,8 +121,7 @@ public partial class StatusDialog : Adw.Window
scrolled.SetOverflow(Overflow.Hidden); scrolled.SetOverflow(Overflow.Hidden);
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
foreach (ConvertError err in convertErrors) foreach (ConvertError err in convertErrors) {
{
sb.AppendLine($"{err.File}: {err.Message}"); sb.AppendLine($"{err.File}: {err.Message}");
} }
Label label = Label.New(sb.ToString()); Label label = Label.New(sb.ToString());
@ -155,15 +147,12 @@ public partial class StatusDialog : Adw.Window
return pageStatus; return pageStatus;
} }
public void Partial(int number, string dest, ConvertError[] convertErrors) public void Partial(int number, string dest, ConvertError[] convertErrors) {
{
StatusPage pagePartial = InitPagePartial(number, dest, convertErrors); StatusPage pagePartial = InitPagePartial(number, dest, convertErrors);
_stack.AddNamed(pagePartial, "page-partial"); SetContent(pagePartial);
_stack.SetVisibleChildName("page-partial");
} }
private StatusPage InitPageError(string error) private StatusPage InitPageError(string error) {
{
StatusPage pageStatus = StatusPage.New(); StatusPage pageStatus = StatusPage.New();
pageStatus.SetTitle("Konvertierung fehlgeschlagen"); pageStatus.SetTitle("Konvertierung fehlgeschlagen");
pageStatus.SetDescription("Es ist ein Fehler aufgetreten."); pageStatus.SetDescription("Es ist ein Fehler aufgetreten.");
@ -206,10 +195,8 @@ public partial class StatusDialog : Adw.Window
return pageStatus; return pageStatus;
} }
public void Error(string error) public void Error(string error) {
{
StatusPage pageError = InitPageError(error); StatusPage pageError = InitPageError(error);
_stack.AddNamed(pageError, "page-error"); SetContent(pageError);
_stack.SetVisibleChildName("page-error");
} }
} }

@ -1,3 +1,23 @@
# Photomator # Photomator
Desktop-Anwendung zum Konvertieren und Schrumpfen von Bildern für die Verwendung im Web. Desktop-Anwendung zum Konvertieren und Schrumpfen von Bildern für die Verwendung im Web.
## Build
### Windows
- install msys2 (mingw-w64) -> libadwaita, gtk4, libwebp
- install .NET 8.0
- install Git for Windows
- add .NET 8.0 and mingw-w64 to PATH
```powershell
git clone https://git.cantorgymnasium.de/gcg/photomator
cd photomator/Photomator
dotnet restore
dotnet publish -r win-x64
```
The installer will be located under `Photomator\bin\Release\net8.0\win-x64\publish`.

3
renovate.json Normal file

@ -0,0 +1,3 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
}