Jank incoming!!! (First UI commit)

This commit is contained in:
ZacAttack
2024-09-23 02:27:15 -07:00
parent 5ad5005d98
commit e959de1236
27 changed files with 1765 additions and 0 deletions

3
.gitignore vendored
View File

@@ -396,3 +396,6 @@ FodyWeavers.xsd
# JetBrains Rider
*.sln.iml
.idea
# Other

15
App.xaml Normal file
View File

@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<Application
x:Class="WinDurango.UI.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WinDurango.UI"
xmlns:controls="using:Microsoft.UI.Xaml.Controls">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

40
App.xaml.cs Normal file
View File

@@ -0,0 +1,40 @@
using Microsoft.UI.Xaml;
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
namespace WinDurango.UI
{
public partial class App : Application
{
private static readonly FileVersionInfo Fvi = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location);
public static readonly string DataDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "WinDurango");
public static readonly MainWindow MainWindow = new();
public static readonly uint Major = (uint)Fvi.ProductMajorPart;
public static readonly uint Minor = (uint)Fvi.ProductMinorPart;
public static readonly uint Patch = (uint)Fvi.ProductBuildPart;
public static readonly string Hash = Fvi.ProductVersion.Split("+")[1].Substring(0, 7);
public static readonly uint VerPacked = (Major << 22) | (Minor << 12) | Patch;
public static readonly string Version = $"{Fvi.ProductMajorPart}.{Fvi.ProductMinorPart}.{Fvi.ProductBuildPart}_{Hash}"; // 1.0 will be when bugs are squashed and everything works correctly.
public static (uint major, uint minor, uint patch) UnpackVersion(uint verPacked)
{
uint major = (verPacked >> 22) & 0x3FF;
uint minor = (verPacked >> 12) & 0x3FF;
uint patch = verPacked & 0xFFF;
return (major, minor, patch);
}
public App()
{
InitializeComponent();
}
protected override void OnLaunched(LaunchActivatedEventArgs args)
{
MainWindow.Activate();
}
}
}

BIN
Assets/icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 KiB

205
Assets/icon.svg Normal file
View File

@@ -0,0 +1,205 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
width="360mm"
height="360mm"
viewBox="0 0 360 360"
version="1.1"
id="svg1"
xml:space="preserve"
sodipodi:docname="WinDurango.svg"
inkscape:export-filename="logo\16.png"
inkscape:export-xdpi="1.128889"
inkscape:export-ydpi="1.128889"
inkscape:version="1.3.2 (091e20e, 2023-11-25, custom)"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns="http://www.w3.org/2000/svg"
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
id="namedview1"
pagecolor="#22b14c"
bordercolor="#000000"
borderopacity="0.25"
inkscape:showpageshadow="2"
inkscape:pageopacity="0.0"
inkscape:pagecheckerboard="0"
inkscape:deskcolor="#d1d1d1"
inkscape:document-units="mm"
showgrid="true"
inkscape:export-bgcolor="#00000000"
inkscape:zoom="0.35355339"
inkscape:cx="627.91082"
inkscape:cy="640.63874"
inkscape:window-width="1600"
inkscape:window-height="829"
inkscape:window-x="1912"
inkscape:window-y="163"
inkscape:window-maximized="1"
inkscape:current-layer="svg1"><inkscape:grid
id="grid86"
units="mm"
originx="0"
originy="0"
spacingx="7.5000002"
spacingy="7.5000002"
empcolor="#0099e5"
empopacity="0.79215686"
color="#0099e5"
opacity="0.14901961"
empspacing="6"
dotted="false"
gridanglex="30"
gridanglez="30"
visible="true" /></sodipodi:namedview><defs
id="defs1"><rect
x="1339.3906"
y="363.3844"
width="2062.1841"
height="316.93054"
id="rect497" /><linearGradient
id="swatch83"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop83" /></linearGradient><linearGradient
id="swatch42"
inkscape:swatch="solid"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop43" /></linearGradient><filter
style="color-interpolation-filters:sRGB"
inkscape:label="Drop Shadow"
id="filter481"
x="-0.091402709"
y="-0.093543364"
width="1.1828054"
height="1.1870867"><feFlood
result="flood"
in="SourceGraphic"
flood-opacity="0.498039"
flood-color="rgb(255,255,255)"
id="feFlood480" /><feGaussianBlur
result="blur"
in="SourceGraphic"
stdDeviation="2.000000"
id="feGaussianBlur480" /><feOffset
result="offset"
in="blur"
dx="0.000000"
dy="0.000000"
id="feOffset480" /><feComposite
result="comp1"
operator="in"
in="flood"
in2="offset"
id="feComposite480" /><feComposite
result="comp2"
operator="over"
in="SourceGraphic"
in2="comp1"
id="feComposite481" /></filter></defs><g
id="layer1"
transform="matrix(1.4374998,0,0,1.4374998,-143.03639,-167.25425)"><path
id="path5"
style="fill:#212121;fill-opacity:1;stroke-width:0.453453"
d="m 334.64661,164.23247 -132.92989,-4.1e-4 c 0,0 -6.058,-0.89568 -11.44184,-5.38304 -5.38384,-4.48736 -36.34905,5.38307 -43.97747,11.219 -7.62888,5.83321 -2.31117,8.52565 -9.68202,13.23791 -7.37087,4.71228 -26.66654,69.77965 -26.66654,69.77965 0,0 -21.664127,63.58734 23.20994,72.33717 0,0 7.01674,-1.70344 16.43812,-12.9232 9.42454,-11.21977 27.82092,-27.14947 37.69482,-27.37436 9.65763,-0.21904 45.06787,-0.008 46.61098,0 h 100.7439 c 5.91388,0 10.07439,-4.16052 10.07439,-10.0744 V 174.30687 c 0,-5.91389 -4.16051,-10.07437 -10.07439,-10.0744 z"
sodipodi:nodetypes="scscscccccssss" /><path
id="rect4"
style="fill:#1f2121;fill-opacity:1;stroke:none;stroke-width:1.68373;stroke-linecap:square;stroke-dasharray:none;stroke-dashoffset:0;paint-order:stroke fill markers"
d="m 203.6334,295.29303 v 10.02713 h -12.65245 a 7.4036329,7.40357 0 0 0 -7.40408,7.40403 v 2.62416 h 20.05653 20.05551 20.05549 20.05656 v -2.62416 a 7.4036329,7.40357 0 0 0 -7.4041,-7.40403 H 243.7444 v -10.02713 h -20.05549 z" /><g
id="g5"
transform="matrix(1.9183886,0,0,1.9633176,93.408043,-126.03525)"
style="fill:#ffffff;fill-opacity:1;filter:url(#filter481)"><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="m 119.99665,155.91808 h -9.49801 c -0.27747,0 -0.50247,0.29366 -0.50247,0.65593 0,0.3622 0.225,0.6559 0.50247,0.6559 h 9.49801 c 0.27756,0 0.50249,-0.2937 0.50249,-0.6559 0,-0.36227 -0.22493,-0.65593 -0.50249,-0.65593 z"
id="path4-7" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="M 88.487741,158.85938 H 75.990927 c -0.277481,0 -0.502481,0.2936 -0.502481,0.6559 0,0.36221 0.225,0.65591 0.502481,0.65591 h 12.496814 c 0.277481,0 0.502484,-0.2937 0.502484,-0.65591 0,-0.3623 -0.225003,-0.6559 -0.502484,-0.6559 z"
id="path5-4" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="M 114.74517,158.85938 H 94.744193 c -0.277478,0 -0.502481,0.2936 -0.502481,0.6559 0,0.36221 0.225003,0.65591 0.502481,0.65591 h 20.000977 c 0.27749,0 0.50249,-0.2937 0.50249,-0.65591 0,-0.3623 -0.225,-0.6559 -0.50249,-0.6559 z"
id="path6" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="M 93.359003,161.80055 H 73.738248 c -0.277482,0 -0.502483,0.2937 -0.502483,0.6559 0,0.36231 0.225001,0.65591 0.502483,0.65591 h 19.620755 c 0.277481,0 0.502484,-0.2936 0.502484,-0.65591 0,-0.3622 -0.225003,-0.6559 -0.502484,-0.6559 z"
id="path7" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.0990624;stroke-dasharray:none"
d="m 114.59617,161.80055 h -14.4515 c -0.359751,0 -0.651472,0.2937 -0.651472,0.6559 0,0.36231 0.291721,0.65591 0.651472,0.65591 h 14.4515 c 0.35976,0 0.65149,-0.2936 0.65149,-0.65591 0,-0.3622 -0.29173,-0.6559 -0.65149,-0.6559 z"
id="path8" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="M 87.402862,170.62436 H 73.738248 c -0.277482,0 -0.502483,0.29371 -0.502483,0.6559 0,0.36221 0.225001,0.6559 0.502483,0.6559 h 13.664614 c 0.277482,0 0.502482,-0.29369 0.502482,-0.6559 0,-0.36219 -0.225,-0.6559 -0.502482,-0.6559 z"
id="path484" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="M 94.16098,167.68309 H 73.738248 c -0.277482,0 -0.502483,0.2937 -0.502483,0.6559 0,0.36221 0.225001,0.65591 0.502483,0.65591 h 20.422656 c 0.277559,0 0.502483,-0.2937 0.502483,-0.65591 0,-0.3622 -0.224924,-0.6559 -0.502407,-0.6559 z"
id="path483" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="M 89.655543,164.74182 H 75.990927 c -0.277481,0 -0.502481,0.2937 -0.502481,0.6559 0,0.36231 0.225,0.6559 0.502481,0.6559 h 13.664616 c 0.277481,0 0.502484,-0.29359 0.502484,-0.6559 0,-0.3622 -0.225003,-0.6559 -0.502484,-0.6559 z"
id="path9" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.0822406;stroke-dasharray:none"
d="M 100.44288,173.57203 H 79.269506 c -0.250465,0 -0.453432,0.29074 -0.453432,0.64951 0,0.35867 0.202967,0.6495 0.453432,0.6495 h 21.173374 c 0.25045,0 0.45343,-0.29083 0.45343,-0.6495 0,-0.35877 -0.20298,-0.64951 -0.45343,-0.64951 z"
id="path10" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="m 89.473043,176.5069 h -8.976677 c -0.277558,0 -0.502481,0.2936 -0.502481,0.6559 0,0.36221 0.224923,0.65591 0.502481,0.65591 h 8.976677 c 0.277557,0 0.502483,-0.2937 0.502483,-0.65591 0,-0.3623 -0.224926,-0.6559 -0.502483,-0.6559 z"
id="path486" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="m 123.44599,173.56563 h -6.9065 c -0.27748,0 -0.50248,0.2936 -0.50248,0.65591 0,0.3622 0.225,0.6559 0.50248,0.6559 h 6.9065 c 0.27756,0 0.50248,-0.2937 0.50248,-0.6559 0,-0.36231 -0.22492,-0.65591 -0.50248,-0.65591 z"
id="path485" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="m 112.18251,173.56563 h -6.9065 c -0.27749,0 -0.50249,0.2936 -0.50249,0.65591 0,0.3622 0.225,0.6559 0.50249,0.6559 h 6.9065 c 0.27755,0 0.50248,-0.2937 0.50248,-0.6559 0,-0.36231 -0.22493,-0.65591 -0.50248,-0.65591 z"
id="path11" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="M 92.807087,179.44807 H 80.496366 c -0.277558,0 -0.502481,0.29371 -0.502481,0.6559 0,0.36231 0.224923,0.6559 0.502481,0.6559 h 12.310721 c 0.277482,0 0.502484,-0.29359 0.502484,-0.6559 0,-0.36219 -0.225002,-0.6559 -0.502484,-0.6559 z"
id="path12" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="M 112.18251,179.44807 H 98.517969 c -0.277559,0 -0.502481,0.29371 -0.502481,0.6559 0,0.36231 0.224922,0.6559 0.502481,0.6559 h 13.664541 c 0.27755,0 0.50248,-0.29359 0.50248,-0.6559 0,-0.36219 -0.22493,-0.6559 -0.50248,-0.6559 z"
id="path13" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="m 121.64384,179.44807 h -5.10435 c -0.27748,0 -0.50248,0.29371 -0.50248,0.6559 0,0.36231 0.225,0.6559 0.50248,0.6559 h 5.10435 c 0.27749,0 0.50248,-0.29359 0.50248,-0.6559 0,-0.36219 -0.22499,-0.6559 -0.50248,-0.6559 z"
id="path14" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.0884533;stroke-dasharray:none"
d="M 107.2074,188.27188 H 80.740969 c -0.286909,0 -0.519409,0.29362 -0.519409,0.65591 0,0.3622 0.2325,0.6559 0.519409,0.6559 H 107.2074 c 0.28683,0 0.51941,-0.2937 0.51941,-0.6559 0,-0.36229 -0.23258,-0.65591 -0.51941,-0.65591 z"
id="path488" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.0884533;stroke-dasharray:none"
d="M 87.880138,185.33061 H 73.755176 c -0.28683,0 -0.519411,0.29371 -0.519411,0.6559 0,0.36221 0.232581,0.65591 0.519411,0.65591 h 14.124962 c 0.28683,0 0.51941,-0.2937 0.51941,-0.65591 0,-0.36219 -0.23258,-0.6559 -0.51941,-0.6559 z"
id="path487" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.0884533;stroke-dasharray:none"
d="m 82.030718,182.45289 h -8.275542 c -0.28683,0 -0.519411,0.2937 -0.519411,0.65591 0,0.3623 0.232581,0.6559 0.519411,0.6559 h 8.275542 c 0.28683,0 0.519413,-0.2936 0.519413,-0.6559 0,-0.36221 -0.232583,-0.65591 -0.519413,-0.65591 z"
id="path15" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="m 125.24813,188.27188 h -15.46668 c -0.27757,0 -0.50249,0.29362 -0.50249,0.65591 0,0.3622 0.22492,0.6559 0.50249,0.6559 h 15.46668 c 0.27755,0 0.50249,-0.2937 0.50249,-0.6559 0,-0.36229 -0.22494,-0.65591 -0.50249,-0.65591 z"
id="path16" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="m 88.529164,191.21315 h -8.032798 c -0.277558,0 -0.502481,0.2936 -0.502481,0.6559 0,0.36221 0.224923,0.65592 0.502481,0.65592 h 8.032798 c 0.277557,0 0.502484,-0.29371 0.502484,-0.65592 0,-0.3623 -0.224927,-0.6559 -0.502484,-0.6559 z"
id="path17" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="M 87.402862,194.15434 H 73.738248 c -0.277482,0 -0.502483,0.2937 -0.502483,0.65588 0,0.36231 0.225001,0.65592 0.502483,0.65592 h 13.664614 c 0.277482,0 0.502482,-0.29361 0.502482,-0.65592 0,-0.36218 -0.225,-0.65588 -0.502482,-0.65588 z"
id="path489" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="m 111.0562,191.21315 h -8.03287 c -0.27749,0 -0.50248,0.2936 -0.50248,0.6559 0,0.36221 0.22499,0.65592 0.50248,0.65592 h 8.03287 c 0.27749,0 0.50248,-0.29371 0.50248,-0.65592 0,-0.3623 -0.225,-0.6559 -0.50248,-0.6559 z"
id="path19" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="M 109.35315,197.09561 H 95.688543 c -0.277485,0 -0.502489,0.29368 -0.502489,0.65589 0,0.3623 0.225004,0.6559 0.502489,0.6559 h 13.664607 c 0.27749,0 0.50248,-0.2936 0.50248,-0.6559 0,-0.36221 -0.22499,-0.65589 -0.50248,-0.65589 z"
id="path490" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="M 91.908223,197.09561 H 78.243685 c -0.277558,0 -0.502481,0.29368 -0.502481,0.65589 0,0.3623 0.224923,0.6559 0.502481,0.6559 h 13.664538 c 0.277559,0 0.502484,-0.2936 0.502484,-0.6559 0,-0.36221 -0.224925,-0.65589 -0.502484,-0.65589 z"
id="path20" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="M 98.995238,200.03686 H 78.243685 c -0.277558,0 -0.502481,0.29371 -0.502481,0.6559 0,0.36221 0.224923,0.65592 0.502481,0.65592 h 20.751553 c 0.277486,0 0.502481,-0.29371 0.502481,-0.65592 0,-0.36219 -0.224995,-0.6559 -0.502481,-0.6559 z"
id="path491" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="m 102.59502,202.97815 h -7.217328 c -0.277559,0 -0.502481,0.29368 -0.502481,0.65589 0,0.36221 0.224922,0.6559 0.502481,0.6559 h 7.217328 c 0.27757,0 0.5025,-0.29369 0.5025,-0.6559 0,-0.36221 -0.22493,-0.65589 -0.5025,-0.65589 z"
id="path21" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="m 88.116941,202.97815 h -9.873256 c -0.277558,0 -0.502481,0.29368 -0.502481,0.65589 0,0.36221 0.224923,0.6559 0.502481,0.6559 h 9.873256 c 0.277559,0 0.502483,-0.29369 0.502483,-0.6559 0,-0.36221 -0.224924,-0.65589 -0.502483,-0.65589 z"
id="path23" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="M 104.2422,155.91808 H 84.241221 c -0.277482,0 -0.502482,0.29366 -0.502482,0.65593 0,0.3622 0.225,0.6559 0.502482,0.6559 H 104.2422 c 0.27756,0 0.50248,-0.2937 0.50248,-0.6559 0,-0.36227 -0.22492,-0.65593 -0.50248,-0.65593 z"
id="path493" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="m 77.98477,155.91808 h -4.246521 c -0.277482,0 -0.502484,0.29366 -0.502484,0.65593 0,0.3622 0.225002,0.6559 0.502484,0.6559 h 4.246521 c 0.277482,0 0.502483,-0.2937 0.502483,-0.6559 0,-0.36227 -0.225001,-0.65593 -0.502483,-0.65593 z"
id="path492" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="m 83.236257,152.97684 h -9.498009 c -0.277482,0 -0.502483,0.29366 -0.502483,0.6559 0,0.36225 0.225001,0.6559 0.502483,0.6559 h 9.498009 c 0.277482,0 0.502482,-0.29365 0.502482,-0.6559 0,-0.36224 -0.225,-0.6559 -0.502482,-0.6559 z"
id="path3" /><path
style="fill:#ffffff;fill-opacity:1;stroke-width:0.087;stroke-dasharray:none"
d="m 99.792715,191.21315 h -8.032868 c -0.277481,0 -0.502481,0.2936 -0.502481,0.6559 0,0.36221 0.225,0.65592 0.502481,0.65592 h 8.032868 c 0.277495,0 0.502495,-0.29371 0.502495,-0.65592 0,-0.3623 -0.225,-0.6559 -0.502495,-0.6559 z"
id="path18" /></g></g></svg>

After

Width:  |  Height:  |  Size: 15 KiB

34
Controls/AppTile.xaml Normal file
View File

@@ -0,0 +1,34 @@
<StackPanel
x:Class="WinDurango.UI.Controls.AppTile"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WinDurango.UI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Margin="10, 10, 10, 10"
>
<Button Width="320" Height="180" Padding="0" Name="startButton" CornerRadius="5, 5, 0, 0" BorderThickness="0" Background="{ThemeResource SystemControlAltLowAcrylicElementBrush}" >
<Image x:Name="appLogo"/>
</Button>
<Expander CornerRadius="0, 0, 5, 5" Width="320" Name="infoExpander">
<Grid Name="expanderControls" Width="320">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Grid Name="expanderButtons" Grid.Row="1">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="2*"/>
</Grid.ColumnDefinitions>
<Button x:Name="unregisterButton" Grid.Column="0" Content="Unregister" HorizontalAlignment="Center" Click="HandleUnregister"/>
<Button x:Name="viewFolderButton" Grid.Column="1" Content="View Folder" HorizontalAlignment="Center" Click="OpenFolder"/>
</Grid>
<StackPanel Grid.Row="0">
<TextBlock x:Name="expanderVersion" Margin="10 0 0 10"/>
</StackPanel>
</Grid>
</Expander>
</StackPanel>

93
Controls/AppTile.xaml.cs Normal file
View File

@@ -0,0 +1,93 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media.Imaging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using Windows.ApplicationModel;
using Windows.ApplicationModel.Core;
using Windows.Foundation;
using Windows.Storage.Streams;
using WinDurango.UI.Dialogs;
using WinDurango.UI.Settings;
using WinDurango.UI.Utils;
namespace WinDurango.UI.Controls
{
public sealed partial class AppTile
{
private Package _package;
private readonly string _familyName;
private async void HandleUnregister(object sender, RoutedEventArgs e)
{
var confirmation = new Confirmation($"Are you sure you want to uninstall {_package.DisplayName}?", "Uninstall?");
Dialog.BtnClicked answer = await confirmation.Show();
if (answer == Dialog.BtnClicked.Yes)
{
if (InstalledPackages.RemoveSymlinks(_familyName))
await Packages.RemovePackage(_package);
}
}
private void OpenFolder(object sender, RoutedEventArgs e)
{
Process.Start(new ProcessStartInfo(_package.InstalledPath) { UseShellExecute = true });
}
public AppTile(string familyName)
{
_familyName = familyName;
this.InitializeComponent();
_package = Packages.GetPackageByFamilyName(_familyName);
var ss = Packages.getSplashScreenPath(_package);
IReadOnlyList<AppListEntry> appListEntries = _package.GetAppListEntries();
AppListEntry firstAppListEntry = appListEntries[0];
if (ss == null)
{
RandomAccessStreamReference logoStream = firstAppListEntry.DisplayInfo.GetLogo(new Size(320, 180));
BitmapImage logoImage = new();
using IRandomAccessStream stream = logoStream.OpenReadAsync().GetAwaiter().GetResult();
logoImage.SetSource(stream);
appLogo.Source = logoImage;
}
else
{
appLogo.Source = new BitmapImage(new Uri(ss));
}
infoExpander.Header = _package.DisplayName;
Flyout rcFlyout = new();
StackPanel rcOptions = new();
Button rcUnregisterButton = new() { Content = "Remove" };
rcUnregisterButton.Tapped += (s, e) =>
{
HandleUnregister(s, e);
};
rcOptions.Children.Add(rcUnregisterButton);
rcFlyout.Content = rcOptions;
expanderVersion.Text = $"Version {_package.Id.Version.Major}.{_package.Id.Version.Minor}.{_package.Id.Version.Build}";
RightTapped += (sender, e) =>
{
rcFlyout.ShowAt(sender as FrameworkElement);
};
startButton.Tapped += async (s, e) =>
{
var hasLaunched = await firstAppListEntry.LaunchAsync();
if (hasLaunched == false)
new NoticeDialog($"Failed to launch \"{_package.DisplayName}\"!");
};
}
}
}

View File

@@ -0,0 +1,30 @@
<UserControl
x:Class="WinDurango.UI.Controls.ContributorInfo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WinDurango.UI.Controls"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300"
d:DesignWidth="400">
<Grid Height="128" Width="256" Background="{ThemeResource SystemControlAltLowAcrylicElementBrush}" BorderBrush="{ThemeResource ControlElevationBorderBrush}" BorderThickness="1" CornerRadius="5">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="77"></ColumnDefinition>
<ColumnDefinition></ColumnDefinition>
</Grid.ColumnDefinitions>
<PersonPicture Name="developerPicture" x:FieldModifier="public" Grid.Row="0" Padding="5 0 0 0" Width="64" Height="64" VerticalAlignment="Center" BorderBrush="{ThemeResource ControlElevationBorderBrush}" BorderThickness="1"></PersonPicture>
<Grid Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition></RowDefinition>
<RowDefinition></RowDefinition>
</Grid.RowDefinitions>
<HyperlinkButton Name="developerName" FontSize="18" x:FieldModifier="public" VerticalAlignment="Bottom" Grid.Row="0" Padding="5 5 5 5" Margin="0 0 0 0"></HyperlinkButton>
<TextBlock Name="developerInfo" x:FieldModifier="public" TextWrapping="WrapWholeWords" VerticalAlignment="Top" Grid.Row="1" HorizontalAlignment="Left" Margin="5 0 0 0"></TextBlock>
</Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,13 @@
using Microsoft.UI.Xaml.Controls;
namespace WinDurango.UI.Controls
{
public sealed partial class ContributorInfo : UserControl
{
public ContributorInfo()
{
this.InitializeComponent();
}
}
}

75
Dialogs/Confirmation.cs Normal file
View File

@@ -0,0 +1,75 @@
using Microsoft.UI.Xaml.Controls;
using System;
using System.Threading.Tasks;
namespace WinDurango.UI.Dialogs
{
public class Confirmation
{
private string _content;
private string _title;
private ContentDialog _confirmationDialog;
public Confirmation(string content, string title = "Information")
{
_content = content;
_title = title;
}
public async Task<Dialog.BtnClicked> Show()
{
_confirmationDialog = new ContentDialog
{
Content = _content,
Title = _title,
PrimaryButtonText = "Yes",
SecondaryButtonText = "No",
DefaultButton = ContentDialogButton.Secondary,
XamlRoot = App.MainWindow.Content.XamlRoot
};
var result = await _confirmationDialog.ShowAsync();
return result switch
{
ContentDialogResult.Primary => Dialog.BtnClicked.Yes,
ContentDialogResult.Secondary => Dialog.BtnClicked.No,
_ => throw new InvalidOperationException("Unexpected result."),
};
}
public string Content
{
get { return _content; }
set
{
_content = value;
UpdateDialog();
}
}
public string Title
{
get { return _title; }
set
{
_title = value;
UpdateDialog();
}
}
private void UpdateDialog()
{
if (_confirmationDialog != null)
{
_confirmationDialog.Content = _content;
_confirmationDialog.Title = _title;
}
}
public void Remove()
{
_confirmationDialog = null;
}
}
}

12
Dialogs/Dialog.cs Normal file
View File

@@ -0,0 +1,12 @@
namespace WinDurango.UI.Dialogs
{
public static class Dialog
{
public enum BtnClicked
{
Yes,
No
}
}
}

67
Dialogs/Notice.cs Normal file
View File

@@ -0,0 +1,67 @@
using Microsoft.UI.Xaml.Controls;
using System;
using System.Threading.Tasks;
namespace WinDurango.UI.Dialogs
{
public class NoticeDialog
{
private string _content;
private string _title;
private ContentDialog _messageDialog;
public NoticeDialog(string content, string title = "Information")
{
_content = content;
_title = title;
}
public async Task Show()
{
_messageDialog = new ContentDialog
{
Content = _content,
Title = _title,
CloseButtonText = "OK",
XamlRoot = App.MainWindow.Content.XamlRoot
};
await _messageDialog.ShowAsync();
}
public string Content
{
get { return _content; }
set
{
_content = value;
UpdateDialog();
}
}
public string Title
{
get { return _title; }
set
{
_title = value;
UpdateDialog();
}
}
private void UpdateDialog()
{
if (_messageDialog != null)
{
_messageDialog.Content = _content;
_messageDialog.Title = _title;
}
}
public void Remove()
{
_messageDialog = null;
}
}
}

80
Dialogs/Progress.cs Normal file
View File

@@ -0,0 +1,80 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
namespace WinDurango.UI.Dialogs
{
public class ProgressDialog : ContentDialog
{
private string _text;
private string _title;
private double _progress;
private bool _isIndeterminate;
private ProgressBar _progressBar;
private TextBlock _textBlock;
public ProgressDialog(string content, string title = "Information", bool isIndeterminate = true)
{
_text = content;
_title = title;
_isIndeterminate = isIndeterminate;
_progressBar = new ProgressBar
{
IsIndeterminate = _isIndeterminate,
Width = 300,
Value = _progress
};
_textBlock = new TextBlock
{
Text = _text,
Margin = new Thickness(0, 10, 0, 0)
};
Content = new StackPanel
{
Children =
{
_progressBar,
_textBlock
},
XamlRoot = App.MainWindow.Content.XamlRoot
};
Title = _title;
XamlRoot = App.MainWindow.Content.XamlRoot;
}
public string Text
{
get { return _text; }
set
{
_text = value;
UpdateDialog();
}
}
public double Progress
{
get { return _progress; }
set
{
_progress = value;
UpdateDialog();
}
}
private void UpdateDialog()
{
if (_textBlock != null)
{
_textBlock.Text = _text;
}
if (_progressBar != null)
{
_progressBar.Value = _progress;
}
}
}
}

55
MainWindow.xaml Normal file
View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<Window
x:Class="WinDurango.UI.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WinDurango.UI"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="32"/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid Grid.Row="0" Loaded="appTitleBar_Loaded" SizeChanged="appTitleBar_SizeChanged" x:Name="appTitleBar">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="48" x:Name="leftPaddingColumn"/>
<ColumnDefinition/>
<ColumnDefinition Width="0" x:Name="rightPaddingColumn"/>
</Grid.ColumnDefinitions>
<Border Grid.Column="0" Margin="15 0 0 0" Width="32" Height="32">
<Image Width="32" Height="32" Source="ms-appx:///Assets/icon.ico"/>
</Border>
<TextBlock
Text="WinDurango"
Style="{StaticResource CaptionTextBlockStyle}"
Grid.Column="1"
VerticalAlignment="Center"
Margin="28,0,0,0"/>
</Grid>
<NavigationView
Grid.Row="1"
IsPaneToggleButtonVisible="true"
PaneDisplayMode="LeftCompact"
IsPaneOpen="false"
IsBackButtonVisible="Collapsed"
ItemInvoked="NavigationInvoked"
x:Name="navView">
<NavigationView.MenuItems>
<NavigationViewItem Icon="AllApps" Content="Home" Tag="AppsListPage"/>
</NavigationView.MenuItems>
<NavigationView.FooterMenuItems>
<NavigationViewItem Content="About" Tag="AboutPage">
<NavigationViewItem.Icon>
<FontIcon Glyph="&#xE946;" />
</NavigationViewItem.Icon>
</NavigationViewItem>
</NavigationView.FooterMenuItems>
<Frame x:Name="contentFrame"/>
</NavigationView>
</Grid>
</Window>

117
MainWindow.xaml.cs Normal file
View File

@@ -0,0 +1,117 @@
using Microsoft.UI.Composition.SystemBackdrops;
using Microsoft.UI.Windowing;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media;
using System;
using WinDurango.UI.Dialogs;
using WinDurango.UI.Pages;
using WinDurango.UI.Settings;
namespace WinDurango.UI
{
public sealed partial class MainWindow : Window
{
public static readonly WdSettings Settings = new();
public readonly string AppName = "WinDurango";
public AppsListPage AppsListPage;
public SettingsPage SettingsPage;
public AboutPage AboutPage;
private void NavigationInvoked(NavigationView sender, NavigationViewItemInvokedEventArgs args)
{
if (args.IsSettingsInvoked == true)
{
contentFrame.Navigate(typeof(SettingsPage));
}
else if (args.InvokedItemContainer is NavigationViewItem item)
{
string tag = item.Tag.ToString();
Type pageType = tag switch
{
"AppsListPage" => typeof(AppsListPage),
"AboutPage" => typeof(AboutPage),
_ => typeof(AppsListPage)
};
if (contentFrame.Navigate(pageType))
{
if (contentFrame.Content is AppsListPage appsList)
{
AppsListPage = appsList;
}
}
}
}
public void ReloadAppList()
{
if (AppsListPage != null)
AppsListPage.InitAppList();
}
public void LoadSettings()
{
switch (Settings.Settings.Theme)
{
case WdSettingsData.ThemeSetting.Mica:
this.SystemBackdrop = new MicaBackdrop() { Kind = MicaKind.Base };
break;
case WdSettingsData.ThemeSetting.MicaAlt:
this.SystemBackdrop = new MicaBackdrop() { Kind = MicaKind.BaseAlt };
break;
case WdSettingsData.ThemeSetting.Fluent:
this.SystemBackdrop = new DesktopAcrylicBackdrop();
break;
case WdSettingsData.ThemeSetting.FluentThin:
this.SystemBackdrop = new DesktopAcrylicBackdrop();
break;
}
}
public MainWindow()
{
this.InitializeComponent();
Title = AppName;
// setup theme and titlebar
AppWindow appWindow = this.AppWindow;
ExtendsContentIntoTitleBar = true;
Microsoft.UI.Composition.Compositor compositor = this.Compositor;
this.Activate();
LoadSettings();
contentFrame.Navigate(typeof(AppsListPage));
AppsListPage = (AppsListPage)contentFrame.Content;
}
private async void appTitleBar_Loaded(object sender, RoutedEventArgs e)
{
// have to do it here otherwise instance error
var devNotice = new NoticeDialog($"This UI is very early in development, and mainly developed by a C# learner... There WILL be bugs, and some things will NOT work...\n\nDevelopers, check MainWindow.xaml.cs for the todolist.", "Important");
await devNotice.Show();
if (ExtendsContentIntoTitleBar)
{
SetupTitleBar();
}
}
private void appTitleBar_SizeChanged(object sender, SizeChangedEventArgs e)
{
if (ExtendsContentIntoTitleBar)
{
SetupTitleBar();
}
}
private void SetupTitleBar()
{
AppWindowTitleBar titleBar = AppWindow.TitleBar;
double scaleAdjustment = appTitleBar.XamlRoot.RasterizationScale;
rightPaddingColumn.Width = new GridLength(titleBar.RightInset / scaleAdjustment);
leftPaddingColumn.Width = new GridLength(titleBar.LeftInset / scaleAdjustment);
}
}
}

30
Pages/AboutPage.xaml Normal file
View File

@@ -0,0 +1,30 @@
<Page
x:Class="WinDurango.UI.Pages.AboutPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WinDurango.UI.Pages"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:appLocal="using:WinDurango.UI"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:controls="using:WinDurango.UI.Controls" xmlns:svg="using:SVGImage.SVG"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel Orientation="Vertical" Padding="10">
<TextBlock Text="Application Info" FontSize="24"/>
<controls:ContributorInfo Margin="0 10 10 10" x:Name="windurango_Info" HorizontalAlignment="Left"/>
<TextBlock Text="UI Lead Developers" FontSize="24"/>
<StackPanel Orientation="Horizontal">
<controls:ContributorInfo Margin="0 10 10 10" x:Name="dexrn_Info" HorizontalAlignment="Left"/>
<controls:ContributorInfo Margin="0 10 10 10" x:Name="danilwhale_Info" HorizontalAlignment="Left"/>
</StackPanel>
<TextBlock Text="Repo Contributors" FontSize="24"/>
<TextBlock>
<Run Text="Coming soon (maybe)"/>
<LineBreak/>
<Run Text="contrib.rocks apparently returns some svg instead of a png..."/>
<LineBreak/>
<Run Text="which makes it hard for me to use here."/>
</TextBlock>
</StackPanel>
</Page>

33
Pages/AboutPage.xaml.cs Normal file
View File

@@ -0,0 +1,33 @@
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Media.Imaging;
using System;
namespace WinDurango.UI.Pages
{
public sealed partial class AboutPage : Page
{
private string getGitHubPfp(string username)
{
return $"https://github.com/{username}.png";
}
public AboutPage()
{
this.InitializeComponent();
dexrn_Info.developerPicture.ProfilePicture = new BitmapImage(new Uri(getGitHubPfp("DexrnZacAttack")));
dexrn_Info.developerName.Content = "DexrnZacAttack";
dexrn_Info.developerName.NavigateUri = new Uri("https://github.com/DexrnZacAttack");
dexrn_Info.developerInfo.Text = "UI design, functionality, learning C#";
danilwhale_Info.developerPicture.ProfilePicture = new BitmapImage(new Uri(getGitHubPfp("danilwhale")));
danilwhale_Info.developerName.Content = "danilwhale";
danilwhale_Info.developerName.NavigateUri = new Uri("https://github.com/danilwhale");
danilwhale_Info.developerInfo.Text = "Refactoring, teaching, bug fixing, etc";
windurango_Info.developerInfo.Text = $"Version {App.Version}";
windurango_Info.developerName.Content = "WinDurango.UI";
windurango_Info.developerName.NavigateUri = new Uri("https://github.com/WinDurango-project/WinDurango.UI");
windurango_Info.developerPicture.ProfilePicture = new BitmapImage(new Uri(getGitHubPfp("WinDurango-project")));
}
}
}

23
Pages/AppsListPage.xaml Normal file
View File

@@ -0,0 +1,23 @@
<Page
x:Class="WinDurango.UI.Pages.AppsListPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WinDurango.UI.Pages"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:controls="using:CommunityToolkit.WinUI.Controls"
mc:Ignorable="d"
>
<controls:DockPanel>
<controls:WrapPanel controls:DockPanel.Dock="Top" x:Name="appList" HorizontalAlignment="Center" />
<Button
controls:DockPanel.Dock="Bottom"
HorizontalAlignment="Center"
VerticalAlignment="Bottom"
Margin="0,0,0,5"
Tapped="installButton_Tapped"
x:Name="installButton">
Install Package
</Button>
</controls:DockPanel>
</Page>

View File

@@ -0,0 +1,95 @@
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Input;
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Principal;
using Windows.Management.Deployment;
using Windows.Storage;
using Windows.Storage.Pickers;
using WinDurango.UI.Controls;
using WinDurango.UI.Dialogs;
using WinDurango.UI.Settings;
using WinDurango.UI.Utils;
namespace WinDurango.UI.Pages
{
public sealed partial class AppsListPage : Page
{
public void InitAppList()
{
appList.Children.Clear();
Dictionary<string, InstalledPackage> installedPackages = InstalledPackages.GetInstalledPackages();
var pm = new PackageManager();
foreach (var installedPackage in installedPackages)
{
if (pm.FindPackageForUser(WindowsIdentity.GetCurrent().User?.Value, installedPackage.Value.FullName) != null)
{
Grid outerGrid = new();
AppTile gameContainer = new(installedPackage.Key);
outerGrid.Children.Add(gameContainer);
appList.Children.Add(outerGrid);
}
}
}
public AppsListPage()
{
InitializeComponent();
InitAppList();
}
private async void installButton_Tapped(object sender, TappedRoutedEventArgs e)
{
var picker = new FolderPicker();
picker.SuggestedStartLocation = PickerLocationId.Desktop;
picker.FileTypeFilter.Add("*");
var hwnd = WinRT.Interop.WindowNative.GetWindowHandle(App.MainWindow);
WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd);
StorageFolder folder = await picker.PickSingleFolderAsync();
if (folder != null)
{
string manifest = Path.Combine(folder.Path + "\\AppxManifest.xml");
string mountFolder = Path.Combine(folder.Path + "\\Mount");
if (File.Exists(manifest))
{
await Packages.InstallPackageAsync(new Uri(manifest, UriKind.Absolute));
}
else
{
// AppxManifest does not exist in that folder
if (Directory.Exists(mountFolder))
{
// there IS a mount folder
if (File.Exists(Path.Combine(mountFolder + "\\AppxManifest.xml")))
{
// there is an AppxManifest inside.
var confirmation = new Confirmation($"There was no AppxManifest found inside the picked folder.\nHowever there was one found inside the Mount folder, would you like to register that?", "Install from Mount?");
if (await confirmation.Show() == Dialog.BtnClicked.Yes)
await Packages.InstallXPackageAsync((folder.Path).ToString());
}
else
{
// there is no AppxManifest inside.
await new NoticeDialog($"AppxManifest does not exist in both {folder.Path} and {mountFolder}", "Error").Show();
}
}
else
{
await new NoticeDialog($"AppxManifest does not exist in {folder.Path} and there is no \"Mount\" folder.", "Error").Show();
}
return;
}
}
}
}
}

30
Pages/SettingsPage.xaml Normal file
View File

@@ -0,0 +1,30 @@
<Page
x:Class="WinDurango.UI.Pages.SettingsPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:WinDurango.UI.Pages"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Grid Padding="10 10 10 10">
<StackPanel Grid.Row="1">
<TextBlock Text="Theme"/>
<DropDownButton
Loaded="OnThemeButtonLoaded"
Name="themeButton"
Content="Use Mica"
HorizontalAlignment="Left"
VerticalAlignment="Top" >
<DropDownButton.Flyout>
<MenuFlyout Placement="Bottom">
<MenuFlyoutItem Text="Mica" Click="OnMicaSelected"/>
<MenuFlyoutItem Text="Mica Alt" Click="OnMicaAltSelected"/>
<MenuFlyoutItem Text="Fluent" Click="OnFluentSelected"/>
</MenuFlyout>
</DropDownButton.Flyout>
</DropDownButton>
</StackPanel>
</Grid>
</Page>

View File

@@ -0,0 +1,51 @@
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using WinDurango.UI.Settings;
namespace WinDurango.UI.Pages
{
public sealed partial class SettingsPage : Page
{
private async void OnMicaSelected(object sender, RoutedEventArgs e)
{
await MainWindow.Settings.SetSetting("Theme", WdSettingsData.ThemeSetting.Mica);
OnThemeButtonLoaded(sender, e);
}
private async void OnMicaAltSelected(object sender, RoutedEventArgs e)
{
await MainWindow.Settings.SetSetting("Theme", WdSettingsData.ThemeSetting.MicaAlt);
OnThemeButtonLoaded(sender, e);
}
private async void OnFluentSelected(object sender, RoutedEventArgs e)
{
await MainWindow.Settings.SetSetting("Theme", WdSettingsData.ThemeSetting.Fluent);
OnThemeButtonLoaded(sender, e);
}
private void OnThemeButtonLoaded(object sender, RoutedEventArgs e)
{
switch (MainWindow.Settings.Settings.Theme)
{
case WdSettingsData.ThemeSetting.Fluent:
themeButton.Content = "Fluent";
break;
case WdSettingsData.ThemeSetting.FluentThin:
themeButton.Content = "Fluent (Thin)";
break;
case WdSettingsData.ThemeSetting.Mica:
themeButton.Content = "Mica";
break;
case WdSettingsData.ThemeSetting.MicaAlt:
themeButton.Content = "Mica (Alt)";
break;
}
}
public SettingsPage()
{
this.InitializeComponent();
}
}
}

View File

@@ -1,2 +1,22 @@
# WinDurango.UI
GUI for WinDurango, which is planned to allow for easy installing/patching among other random stuff I decide lmfao
> [!NOTE]
> This does nothing more than provide a GUI for easily registering and patching packages with WinDurango.
> Additionally, I don't know much about C#... so the code is probably very messy.
## Todo
- [X] Make the applist not go offscreen (lol)
- [ ] Applist pages
- [ ] Fix icon in the titlebar
- [ ] Allow for search
- [ ] Resize content to fit to screen
- [ ] Allow for package removal from UI instead of just completely uninstalling, as well as allowing for any existing installed package to be added
- [ ] Compiling the DLLs and patching automatically
- [ ] Dropdown button instead of just an install button (so you can install without symlinking dlls, add existing packages, etc)
- [ ] Repo contributors on the about screen
- [ ] Get Fluent Thin working
- [ ] Add versioning to the InstalledPackages json
- [ ] Make the Package stuff not rely on UI so much, handle that somewhere else.
- [ ] Fix crash when installation errors
- [ ] Cleanup, lots and lots of it.

View File

@@ -0,0 +1,173 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Security.Policy;
using System.Text.Json;
using Windows.ApplicationModel;
using WinDurango.UI.Utils;
namespace WinDurango.UI.Settings
{
public class InstalledPackage
{
public string FullName { get; set; }
public List<string> SymlinkedDLLs { get; set; }
}
public abstract class InstalledPackages
{
public static void RemoveInstalledPackage(Package pkg)
{
string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "WinDurango");
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
string filePath = Path.Combine(path, "InstalledPackages.json");
if (!File.Exists(filePath))
{
Debug.WriteLine("Could not get the list of installed packages!");
using (StreamWriter writer = File.CreateText(filePath))
{
writer.WriteLine("{}");
}
}
string json = File.ReadAllText(filePath);
var installedPkgs = JsonSerializer.Deserialize<Dictionary<string, InstalledPackage>>(json) ?? new Dictionary<string, InstalledPackage>();
if (installedPkgs.TryGetValue(pkg.Id.FamilyName, out var package) && package.FullName == pkg.Id.FullName)
{
installedPkgs.Remove(pkg.Id.FamilyName);
}
else
{
Debug.WriteLine($"Couldn't uninstall {pkg.Id.FamilyName} as it was not found in the package list.");
return;
}
string updated = JsonSerializer.Serialize(installedPkgs, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(filePath, updated);
}
public static Dictionary<string, InstalledPackage> GetInstalledPackages()
{
if (!Directory.Exists(App.DataDir))
{
Directory.CreateDirectory(App.DataDir);
}
string filePath = Path.Combine(App.DataDir, "InstalledPackages.json");
if (!File.Exists(filePath))
{
Debug.WriteLine("Could not get the list of installed packages!");
using (StreamWriter writer = File.CreateText(filePath))
{
writer.WriteLine("{}");
}
}
string json = File.ReadAllText(filePath);
Dictionary<string, InstalledPackage> installedPkgs = JsonSerializer.Deserialize<Dictionary<string, InstalledPackage>>(json)
?? new Dictionary<string, InstalledPackage>();
return installedPkgs;
}
public static void SaveInstalledPackages(Dictionary<string, InstalledPackage> installedPkgs)
{
string json = JsonSerializer.Serialize(installedPkgs, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(Path.Combine(App.DataDir, "InstalledPackages.json"), json);
}
public static void UpdateInstalledPackage(string familyName, InstalledPackage installedPkg)
{
Dictionary<string, InstalledPackage> installedPkgs = GetInstalledPackages();
installedPkgs[familyName] = installedPkg;
SaveInstalledPackages(installedPkgs);
}
public static bool RemoveSymlinks(string familyName)
{
var pkg = GetInstalledPackage(familyName).Value;
var pkgMount = Packages.GetPackageByFamilyName(pkg.familyName).EffectivePath;
if (Directory.Exists(pkgMount))
{
foreach (var symlink in pkg.installedPackage.SymlinkedDLLs)
{
string symlinkPath = Path.Combine(pkgMount, symlink);
if (File.Exists(symlinkPath))
{
var attributes = File.GetAttributes(symlinkPath);
if (attributes.HasFlag(FileAttributes.ReparsePoint))
{
File.Delete(symlinkPath);
}
}
}
}
else
{
return false;
}
return true;
}
public static (string familyName, InstalledPackage installedPackage)? GetInstalledPackage(string familyName)
{
Dictionary<string, InstalledPackage> installedPkgs = GetInstalledPackages();
if (installedPkgs.TryGetValue(familyName, out InstalledPackage installedPkg))
return (familyName, installedPkg);
// this should never happen (but in case it does)
throw new KeyNotFoundException($"{familyName} was not found in the installed packages list.");
}
public static void AddInstalledPackage(Package package)
{
string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "WinDurango");
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
string filePath = Path.Combine(path, "InstalledPackages.json");
if (!File.Exists(filePath))
{
Debug.WriteLine("Could not get the list of installed packages!");
using (StreamWriter writer = File.CreateText(filePath))
{
writer.WriteLine("{}");
}
}
string json = File.ReadAllText(filePath);
var installedPkgs = JsonSerializer.Deserialize<Dictionary<string, InstalledPackage>>(json) ?? new Dictionary<string, InstalledPackage>();
if (installedPkgs.ContainsKey(package.Id.FamilyName))
{
return;
}
installedPkgs[package.Id.FamilyName] = new InstalledPackage
{
FullName = package.Id.FullName,
SymlinkedDLLs = new List<string>()
};
string updated = JsonSerializer.Serialize(installedPkgs, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(filePath, updated);
}
}
}

128
Settings/Settings.cs Normal file
View File

@@ -0,0 +1,128 @@
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text.Json;
using System.Threading.Tasks;
namespace WinDurango.UI.Settings;
public class WdSettingsData
{
public enum ThemeSetting
{
Fluent,
FluentThin,
Mica,
MicaAlt
}
public uint SaveVersion { get; set; }
public ThemeSetting Theme { get; set; }
}
public class WdSettings
{
private readonly string _settingsFile = Path.Combine(App.DataDir, "Settings.json");
public WdSettingsData Settings { get; private set; }
public WdSettings()
{
if (!Directory.Exists(App.DataDir))
Directory.CreateDirectory(App.DataDir);
if (File.Exists(_settingsFile))
{
try
{
string json = File.ReadAllText(_settingsFile);
WdSettingsData loadedSettings = JsonSerializer.Deserialize<WdSettingsData>(json);
if (loadedSettings != null)
{
if (loadedSettings.SaveVersion > App.VerPacked)
{
BackupSettings();
GenerateSettings();
}
Settings = loadedSettings;
}
}
catch (Exception ex)
{
Debug.WriteLine("Error loading settings: " + ex.Message);
BackupSettings();
GenerateSettings();
}
}
else
{
BackupSettings();
GenerateSettings();
}
}
private void BackupSettings()
{
File.Move(_settingsFile, _settingsFile + ".old_" + ((DateTimeOffset)DateTime.UtcNow).ToUnixTimeMilliseconds().ToString());
}
private async void GenerateSettings()
{
WdSettingsData defaultSettings = GetDefaults();
Settings = defaultSettings;
await SaveSettings();
}
private static WdSettingsData GetDefaults()
{
return new WdSettingsData
{
SaveVersion = App.VerPacked,
Theme = WdSettingsData.ThemeSetting.Fluent
};
}
public void MigrateSettings()
{
// unused for now
}
public async Task SaveSettings()
{
try
{
Settings.SaveVersion = App.VerPacked;
JsonSerializerOptions options = new() { WriteIndented = true };
string json = JsonSerializer.Serialize(Settings, options);
await File.WriteAllTextAsync(_settingsFile, json);
App.MainWindow.LoadSettings();
}
catch (Exception ex)
{
Debug.WriteLine("Error saving settings: " + ex.Message);
}
}
public async Task SetSetting(string setting, object value)
{
PropertyInfo property = typeof(WdSettingsData).GetProperty(setting);
if (property != null && property.CanWrite)
{
try
{
property.SetValue(Settings, Convert.ChangeType(value, property.PropertyType));
await SaveSettings();
}
catch (Exception ex)
{
Debug.WriteLine($"Error setting {setting}: " + ex.Message);
}
}
else
{
Debug.WriteLine($"Property {setting} does not exist or is read only.");
}
}
}

238
Utils/Packages.cs Normal file
View File

@@ -0,0 +1,238 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Security.Principal;
using System.Threading.Tasks;
using System.Xml.Linq;
using Windows.ApplicationModel;
using Windows.Management.Deployment;
using WinDurango.UI.Dialogs;
using WinDurango.UI.Settings;
namespace WinDurango.UI.Utils
{
public abstract class Packages
{
// TODO: Make these methods not use the GUI, instead just throw an exception and catch it in the area where the method is actually invoked.
public static IEnumerable<Package> GetInstalledPackages()
{
var sid = WindowsIdentity.GetCurrent().User?.Value;
var pm = new PackageManager();
return pm.FindPackagesForUser(sid);
}
public enum XvdMode
{
CreateSymlinks,
DontUse
};
public static async Task InstallXPackageAsync(string dir, XvdMode mode = XvdMode.CreateSymlinks)
{
Debug.WriteLine(dir);
string mountDir = Path.Combine(dir, "Mount");
string exvdDir = Path.Combine(dir, "EmbeddedXvd");
bool hasExvd = Directory.Exists(exvdDir);
if (!Directory.Exists(mountDir))
{
await new NoticeDialog($"{mountDir} was not found.", "Error").Show();
return;
}
string package = await InstallPackageAsync(new Uri(mountDir + "\\AppxManifest.xml", UriKind.Absolute));
var newPackage = InstalledPackages.GetInstalledPackage(package).Value;
if (hasExvd)
{
foreach (string filePath in Directory.GetFiles(Path.Combine(exvdDir + "\\Windows\\System32")))
{
string fileName = Path.GetFileName(filePath);
if (File.Exists(Path.Combine(mountDir, fileName)))
continue;
FileSystemInfo symlink = File.CreateSymbolicLink(Path.Combine(mountDir, fileName), filePath);
newPackage.installedPackage.SymlinkedDLLs.Add(symlink.Name);
InstalledPackages.UpdateInstalledPackage(newPackage.familyName, newPackage.installedPackage);
}
}
}
public static string? getSplashScreenPath(Package pkg)
{
try
{
string installPath = pkg.InstalledPath;
string manifestPath = Path.Combine(installPath, "AppxManifest.xml");
if (!File.Exists(manifestPath))
return null;
string manifest;
using (var stream = File.OpenRead(manifestPath))
{
var reader = new StreamReader(stream);
manifest = reader.ReadToEnd();
}
XDocument doc = XDocument.Parse(manifest);
XElement package = doc.Descendants().FirstOrDefault(e => e.Name.LocalName == "Package");
if (package == null) return null;
XElement applications = package.Descendants().FirstOrDefault(e => e.Name.LocalName == "Applications");
if (applications == null) return null;
XElement application = applications.Descendants().FirstOrDefault(e => e.Name.LocalName == "Application");
if (application == null) return null;
XElement visualElements = application.Descendants().FirstOrDefault(e => e.Name.LocalName == "VisualElements");
if (visualElements == null) return null;
XElement splashScreen = visualElements.Descendants().FirstOrDefault(e => e.Name.LocalName == "SplashScreen");
if (splashScreen == null) return null;
string imagePath = splashScreen.Attribute("Image")?.Value;
if (imagePath == null) return null;
string splashScreenPath = Path.Combine(installPath, imagePath);
return splashScreenPath;
}
catch
{
return null;
}
}
public static async Task<string?> InstallPackageAsync(Uri appxManifestUri)
{
// TODO: strip UI
if (!File.Exists(appxManifestUri.AbsolutePath))
{
await new NoticeDialog($"{appxManifestUri.AbsolutePath} was not found.", "Error").Show();
return null;
}
var status = new ProgressDialog($"Installing package...", "Installing", false);
App.MainWindow.DispatcherQueue.TryEnqueue(async () => await status.ShowAsync());
PackageManager pm = new PackageManager();
try
{
status.Text = "Reading manifest...";
string manifest;
await using (var stream = File.OpenRead(appxManifestUri.AbsolutePath))
{
var reader = new StreamReader(stream);
manifest = await reader.ReadToEndAsync();
}
XDocument doc = XDocument.Parse(manifest);
XElement package = doc.Descendants().FirstOrDefault(e => e.Name.LocalName == "Package");
XElement identity = package?.Descendants().FirstOrDefault(e => e.Name.LocalName == "Identity");
string pkgName = identity.Attribute("Name")?.Value;
string pkgPublisher = identity.Attribute("Publisher")?.Value;
status.Progress = 20.0;
status.Text = $"Checking if {pkgName} is already installed...";
var sid = WindowsIdentity.GetCurrent().User?.Value;
var installedPackages = await Task.Run(() => pm.FindPackagesForUser(sid, pkgName, pkgPublisher));
if (installedPackages.Any())
{
status.Hide();
await new NoticeDialog($"{pkgName} is already installed.", "Error").Show();
return null;
}
status.Progress = 40.0;
status.Text = $"Installing {pkgName}...";
var deployment = await pm.RegisterPackageAsync(appxManifestUri, null, DeploymentOptions.DevelopmentMode);
status.Progress = 60.0;
status.Text = $"Getting app info...";
var recentPkg = GetMostRecentlyInstalledPackage();
status.Text = $"Updating app list...";
status.Progress = 80.0;
InstalledPackages.AddInstalledPackage(recentPkg);
status.Progress = 90.0;
App.MainWindow.ReloadAppList();
status.Progress = 100.0;
status.Hide();
await new NoticeDialog($"{recentPkg.Id.Name} was installed! :)").Show();
return recentPkg.Id.FamilyName;
}
catch (Exception e)
{
// we're fucked :(
status.Hide();
await new NoticeDialog($"{appxManifestUri} failed to install: {e.Message}", "Error").Show();
return null;
}
}
public static async Task RemovePackage(Package package)
{
var status = new ProgressDialog($"Uninstalling {package.DisplayName}...", "Uninstalling", false);
App.MainWindow.DispatcherQueue.TryEnqueue(async () => await status.ShowAsync());
PackageManager pm = new PackageManager();
try
{
var undeployment = await pm.RemovePackageAsync(package.Id.FullName, RemovalOptions.PreserveApplicationData);
status.Progress = 50.0;
InstalledPackages.RemoveInstalledPackage(package);
status.Progress = 100.0;
status.Hide();
await new NoticeDialog($"{package.DisplayName} was uninstalled.").Show();
App.MainWindow.ReloadAppList();
}
catch (Exception ex)
{
status.Hide();
await new NoticeDialog($"{package.DisplayName} failed to uninstall: {ex.Message}", "Error!").Show();
}
return;
}
public static Package GetPackageByFamilyName(string familyName)
{
var packageManager = new PackageManager();
var packages = packageManager.FindPackagesForUser(null, familyName);
if (packages == null || !packages.Any())
{
return null;
}
return packages.First();
}
public static Package GetMostRecentlyInstalledPackage()
{
var sid = WindowsIdentity.GetCurrent().User?.Value;
var pm = new PackageManager();
var packages = pm.FindPackagesForUser(sid);
if (!packages.Any())
return null;
var newestPackage = packages.Last();
return newestPackage;
}
}
}

86
WinDurango.csproj Normal file
View File

@@ -0,0 +1,86 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net8.0-windows10.0.22621.0</TargetFramework>
<TargetPlatformMinVersion>10.0.19041.0</TargetPlatformMinVersion>
<RootNamespace>WinDurango.UI</RootNamespace>
<ApplicationManifest>app.manifest</ApplicationManifest>
<Platforms>x86;x64;ARM64</Platforms>
<RuntimeIdentifiers>win-x86;win-x64;win-arm64</RuntimeIdentifiers>
<UseWinUI>True</UseWinUI>
<WindowsAppSDKSelfContained>true</WindowsAppSDKSelfContained>
<IncludeSourceRevisionInInformationalVersion>true</IncludeSourceRevisionInInformationalVersion>
<XamlDebuggingInformation>True</XamlDebuggingInformation>
<DisableXbfLineInfo>False</DisableXbfLineInfo>
<ApplicationIcon>Assets\icon.ico</ApplicationIcon>
<SupportedOSPlatformVersion>10.0.19041.0</SupportedOSPlatformVersion>
<Version>0.0.1</Version>
<AssemblyVersion>$(Version)</AssemblyVersion>
</PropertyGroup>
<ItemGroup>
<Compile Remove="Theming\**" />
<Content Remove="Theming\**" />
<EmbeddedResource Remove="Theming\**" />
<None Remove="Theming\**" />
<Page Remove="Theming\**" />
<PRIResource Remove="Theming\**" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Controls\GameContainer.cs" />
</ItemGroup>
<ItemGroup>
<None Remove="Assets\icon.svg" />
<None Remove="Controls\AppTile.xaml" />
<None Remove="Controls\ContributorInfo.xaml" />
<None Remove="Controls\NewFile1.txt" />
<None Remove="Pages\AboutPage.xaml" />
<None Remove="Pages\AppsListPage.xaml" />
<None Remove="Pages\SettingsPage.xaml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="CommunityToolkit.Uwp.Controls.Primitives" Version="8.1.240821" />
<PackageReference Include="DotNetProjects.SVGImage" Version="5.2.1" />
<PackageReference Include="Microsoft.UI.Xaml" Version="2.8.6" />
<PackageReference Include="Microsoft.Windows.SDK.BuildTools" Version="10.0.22621.756" />
<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.5.240802000" />
<PackageReference Include="System.DirectoryServices" Version="8.0.0" />
<PackageReference Include="System.DirectoryServices.AccountManagement" Version="8.0.0" />
<PackageReference Include="System.Drawing.Common" Version="8.0.8" />
<Manifest Include="$(ApplicationManifest)" />
</ItemGroup>
<ItemGroup>
<Content Update="Assets\icon.ico">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Page Update="Controls\ContributorInfo.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Pages\AboutPage.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Pages\SettingsPage.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<Page Update="Pages\AppsListPage.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Update="Controls\AppTile.xaml">
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
</Project>

19
app.manifest Normal file
View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="WinDurango.app"/>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- The ID below informs the system that this application is compatible with OS features first introduced in Windows 10.
It is necessary to support features in unpackaged applications, for example the custom titlebar implementation.
For more info see https://docs.microsoft.com/windows/apps/windows-app-sdk/use-windows-app-sdk-run-time#declare-os-compatibility-in-your-application-manifest -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness>
</windowsSettings>
</application>
</assembly>