Skip to content

Commit db17ceb

Browse files
committed
adding online templates downloader, check if template exists in %APPDATA%/UnityHub/Templates,
1 parent 8513777 commit db17ceb

File tree

3 files changed

+180
-46
lines changed

3 files changed

+180
-46
lines changed
Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,36 @@
1-
namespace UnityLauncherPro.Data
1+
using System.ComponentModel;
2+
3+
namespace UnityLauncherPro.Data
24
{
3-
public class OnlineTemplateItem
5+
public class OnlineTemplateItem : INotifyPropertyChanged
46
{
7+
private bool _isDownloaded;
8+
59
public string Name { get; set; }
610
public string Description { get; set; }
711
public string RenderPipeline { get; set; }
812
public string Type { get; set; } // Core, Learning, Sample,
913
public string PreviewImageURL { get; set; }
1014
public string TarBallURL { get; set; }
15+
16+
public bool IsDownloaded
17+
{
18+
get { return _isDownloaded; }
19+
set
20+
{
21+
if (_isDownloaded != value)
22+
{
23+
_isDownloaded = value;
24+
OnPropertyChanged(nameof(IsDownloaded));
25+
}
26+
}
27+
}
28+
29+
public event PropertyChangedEventHandler PropertyChanged;
30+
31+
protected virtual void OnPropertyChanged(string propertyName)
32+
{
33+
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
34+
}
1135
}
1236
}

UnityLauncherPro/NewProject.xaml

Lines changed: 60 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
xmlns:data="clr-namespace:UnityLauncherPro.Data"
88
d:DataContext="{d:DesignInstance Type=local:NewProject}"
99
mc:Ignorable="d"
10-
Title="Create New Project" Height="480" Width="640" Background="{DynamicResource ThemeDarkestBackground}" PreviewKeyDown="Window_PreviewKeyDown" ResizeMode="NoResize" WindowStartupLocation="CenterOwner" ShowInTaskbar="True">
10+
Title="Create New Project" Height="520" Width="640" Background="{DynamicResource ThemeDarkestBackground}" PreviewKeyDown="Window_PreviewKeyDown" ResizeMode="NoResize" WindowStartupLocation="CenterOwner" ShowInTaskbar="True">
1111

1212
<Grid>
1313
<Grid.ColumnDefinitions>
@@ -24,7 +24,7 @@
2424
</DataGrid.Columns>
2525
</DataGrid>
2626

27-
<Grid HorizontalAlignment="Stretch" Margin="0,3,0,0">
27+
<Grid HorizontalAlignment="Stretch" Margin="0,20,0,0">
2828
<Grid.ColumnDefinitions>
2929
<ColumnDefinition Width="35*"/>
3030
<ColumnDefinition Width="15*"/>
@@ -50,7 +50,7 @@
5050
<CheckBox Grid.Column="3" x:Name="chkForceDX11" Content="DX11" ToolTip="Use DX11 instead of DX12" Margin="6,0,0,0" IsChecked="False" Checked="chkForceDX11_Checked" Unchecked="chkForceDX11_Checked"/>
5151
</Grid>
5252

53-
<Label x:Name="lblNewProjectFolderLabel" Grid.Column="0" Content="Project Root Folder:" Foreground="{DynamicResource ThemeButtonForeground}" Margin="0" Padding="0,5,5,0" />
53+
<Label x:Name="lblNewProjectFolderLabel" Grid.Column="0" Content="Project Root Folder:" Foreground="{DynamicResource ThemeButtonForeground}" Margin="0,8,0,0" Padding="0,5,5,0" />
5454
<Grid HorizontalAlignment="Stretch" Margin="0,3,0,0">
5555
<Grid.ColumnDefinitions>
5656
<ColumnDefinition Width="18*"/>
@@ -63,7 +63,7 @@
6363
</Grid>
6464

6565
<!--<Label x:Name="lblNewProjectFolder" Content="(folder)" Foreground="{DynamicResource ThemeButtonForegroundDisabled}" Margin="0" FontSize="10" Padding="5,0,5,3" />-->
66-
<Grid HorizontalAlignment="Stretch" Margin="0,24,0,0">
66+
<Grid HorizontalAlignment="Stretch" Margin="0,32,0,0">
6767
<Grid.ColumnDefinitions>
6868
<ColumnDefinition Width="0.7*"/>
6969
<ColumnDefinition Width="*"/>
@@ -86,40 +86,9 @@
8686
</StackPanel>
8787
<StackPanel Orientation="Vertical" Margin="0,3,10,3" Grid.Column="1">
8888
<Label Content="Unity Online Templates" Foreground="{DynamicResource ThemeButtonForeground}" Margin="0,0,0,3" Padding="5,5,5,3" />
89-
<!-- search box -->
90-
<Grid Background="{DynamicResource ThemeTextBoxBackground}" VerticalAlignment="Top" Margin="6,5,0,0" Height="20" >
91-
<TextBlock Margin="3,2" MinWidth="100" Text="Filter" Foreground="{DynamicResource ThemeSearchPlaceholder}" Height="24">
92-
<TextBlock.Style>
93-
<Style TargetType="TextBlock">
94-
<Setter Property="Visibility" Value="Collapsed" />
95-
<Style.Triggers>
96-
<DataTrigger Binding="{Binding Text, ElementName=txtFilterTemplates}" Value="">
97-
<Setter Property="Visibility" Value="Visible" />
98-
</DataTrigger>
99-
</Style.Triggers>
100-
</Style>
101-
</TextBlock.Style>
102-
</TextBlock>
103-
<TextBox MinWidth="100" CaretBrush="{DynamicResource ThemeSearchCaret}" x:Name="txtFilterTemplates" Background="Transparent" BorderBrush="{x:Null}" Foreground="{DynamicResource ThemeSearchForeground}" SelectionBrush="{DynamicResource ThemeSearchSelection}" BorderThickness="0" Margin="2,2,0,0" UndoLimit="64" />
104-
<Button x:Name="btnClearUpdatesSearch" Style="{StaticResource {x:Static ToolBar.ButtonStyleKey}}" BorderThickness="0" HorizontalAlignment="Right" VerticalAlignment="Top" Height="23" Width="23" Background="Transparent" Padding="0,2" Visibility="Visible" BorderBrush="{x:Null}" >
105-
<TextBlock Text="" FontSize="8" Foreground="{DynamicResource ThemeSearchClose}" Padding="5,3,4,4" HorizontalAlignment="Center">
106-
<TextBlock.Style>
107-
<Style TargetType="{x:Type TextBlock}">
108-
<Style.Triggers>
109-
<DataTrigger Binding="{Binding Text, ElementName=txtFilterTemplates}" Value="">
110-
<Setter Property="Visibility" Value="Hidden"/>
111-
</DataTrigger>
112-
<DataTrigger Binding="{Binding Text, ElementName=txtFilterTemplates}" Value="{x:Null}">
113-
<Setter Property="Visibility" Value="Hidden"/>
114-
</DataTrigger>
115-
</Style.Triggers>
116-
</Style>
117-
</TextBlock.Style>
118-
</TextBlock>
119-
</Button>
120-
</Grid>
12189

122-
<ListBox x:Name="listOnlineTemplates" Margin="6,5,0,0" Height="310"
90+
91+
<ListBox x:Name="listOnlineTemplates" Margin="6,5,0,0" Height="411"
12392
Background="{DynamicResource ThemeMainBackgroundColor}"
12493
Foreground="{DynamicResource ThemeButtonForeground}"
12594
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
@@ -140,11 +109,60 @@
140109
<ListBox.ItemTemplate>
141110
<DataTemplate>
142111
<StackPanel Orientation="Vertical" Margin="3">
143-
<Image Source="{Binding PreviewImageURL}"
144-
Margin="0,0,0,5"
145-
ToolTip="{Binding Description}"
146-
Stretch="Uniform"
147-
HorizontalAlignment="Stretch"/>
112+
<Grid>
113+
<Image Source="{Binding PreviewImageURL}"
114+
Margin="0,0,0,5"
115+
ToolTip="{Binding Description}"
116+
Stretch="Uniform"
117+
HorizontalAlignment="Stretch"/>
118+
<Button x:Name="btnDownloadTemplate"
119+
ToolTip="Download Template"
120+
Content=""
121+
Height="24"
122+
Width="24"
123+
HorizontalAlignment="Right"
124+
VerticalAlignment="Top"
125+
FontSize="14"
126+
Margin="2"
127+
Padding="0"
128+
BorderBrush="{x:Null}"
129+
Opacity="0.85"
130+
Click="btnDownloadTemplate_Click"
131+
Tag="{Binding}">
132+
<Button.Style>
133+
<Style TargetType="Button" BasedOn="{StaticResource CustomButton}">
134+
<Setter Property="Visibility" Value="Visible"/>
135+
<Style.Triggers>
136+
<DataTrigger Binding="{Binding IsDownloaded}" Value="True">
137+
<Setter Property="Visibility" Value="Collapsed"/>
138+
</DataTrigger>
139+
</Style.Triggers>
140+
</Style>
141+
</Button.Style>
142+
</Button>
143+
<!-- Downloaded indicator badge -->
144+
<Border Background="#CC2E7D32"
145+
CornerRadius="3"
146+
HorizontalAlignment="Right"
147+
VerticalAlignment="Top"
148+
Padding="4,2"
149+
Margin="2">
150+
<Border.Style>
151+
<Style TargetType="Border">
152+
<Setter Property="Visibility" Value="Collapsed"/>
153+
<Style.Triggers>
154+
<DataTrigger Binding="{Binding IsDownloaded}" Value="True">
155+
<Setter Property="Visibility" Value="Visible"/>
156+
</DataTrigger>
157+
</Style.Triggers>
158+
</Style>
159+
</Border.Style>
160+
<TextBlock Text=""
161+
FontSize="10"
162+
FontWeight="Bold"
163+
Foreground="White"/>
164+
</Border>
165+
</Grid>
148166
<TextBlock Text="{Binding Name}"
149167
FontWeight="Bold"
150168
TextWrapping="Wrap"

UnityLauncherPro/NewProject.xaml.cs

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,10 @@ private List<OnlineTemplateItem> ParseTemplatesFromJson(string json)
529529

530530
try
531531
{
532+
// Get templates directory path
533+
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
534+
string templatesPath = Path.Combine(appDataPath, "UnityHub", "Templates");
535+
532536
// Find the edges array
533537
int edgesStart = json.IndexOf("\"edges\":");
534538
if (edgesStart == -1) return templates;
@@ -547,16 +551,37 @@ private List<OnlineTemplateItem> ParseTemplatesFromJson(string json)
547551
string nodeJson = json.Substring(nodeStart, nodeEnd - nodeStart + 1);
548552

549553
// Parse individual fields
554+
var tarballUrl = ExtractNestedJsonString(nodeJson, "\"tarball\"", "\"url\"");
555+
550556
var template = new OnlineTemplateItem
551557
{
552558
Name = ExtractJsonString(nodeJson, "\"name\""),
553559
Description = ExtractJsonString(nodeJson, "\"description\""),
554560
Type = ExtractJsonString(nodeJson, "\"type\""),
555561
RenderPipeline = ExtractJsonString(nodeJson, "\"renderPipeline\""),
556562
PreviewImageURL = ExtractNestedJsonString(nodeJson, "\"previewImage\"", "\"url\"") ?? "pack://application:,,,/Images/icon.png",
557-
TarBallURL = ExtractNestedJsonString(nodeJson, "\"tarball\"", "\"url\"")
563+
TarBallURL = tarballUrl,
564+
IsDownloaded = false
558565
};
559566

567+
// Check if template file already exists
568+
if (!string.IsNullOrEmpty(tarballUrl) && Directory.Exists(templatesPath))
569+
{
570+
try
571+
{
572+
string fileName = Path.GetFileName(new Uri(tarballUrl).LocalPath);
573+
if (!string.IsNullOrEmpty(fileName))
574+
{
575+
string filePath = Path.Combine(templatesPath, fileName);
576+
template.IsDownloaded = File.Exists(filePath);
577+
}
578+
}
579+
catch
580+
{
581+
// If URL parsing fails, keep IsDownloaded as false
582+
}
583+
}
584+
560585
templates.Add(template);
561586
currentPos = nodeEnd + 1;
562587
}
@@ -568,7 +593,6 @@ private List<OnlineTemplateItem> ParseTemplatesFromJson(string json)
568593

569594
return templates;
570595
}
571-
572596
private string ExtractJsonString(string json, string key)
573597
{
574598
int keyIndex = json.IndexOf(key + ":");
@@ -683,5 +707,73 @@ private void listOnlineTemplates_SelectionChanged(object sender, SelectionChange
683707
}
684708
}
685709

710+
private async void btnDownloadTemplate_Click(object sender, RoutedEventArgs e)
711+
{
712+
var button = sender as Button;
713+
if (button?.Tag is OnlineTemplateItem template)
714+
{
715+
if (string.IsNullOrEmpty(template.TarBallURL))
716+
{
717+
Tools.SetStatus("No download URL available for this template");
718+
return;
719+
}
720+
721+
try
722+
{
723+
// Disable button during download
724+
button.IsEnabled = false;
725+
button.Content = "⏳";
726+
727+
// Create templates directory in %APPDATA%\UnityHub\Templates
728+
string appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
729+
string templatesPath = Path.Combine(appDataPath, "UnityHub", "Templates");
730+
731+
if (!Directory.Exists(templatesPath))
732+
{
733+
Directory.CreateDirectory(templatesPath);
734+
}
735+
736+
// Extract original filename from URL
737+
string fileName = Path.GetFileName(new Uri(template.TarBallURL).LocalPath);
738+
if (string.IsNullOrEmpty(fileName))
739+
{
740+
// Fallback to sanitized template name if URL doesn't have a filename
741+
string safeFileName = string.Join("_", template.Name.Split(Path.GetInvalidFileNameChars()));
742+
fileName = $"{safeFileName}.tgz";
743+
}
744+
745+
string targetFilePath = Path.Combine(templatesPath, fileName);
746+
747+
// Download the template (overwrite if exists)
748+
using (var client = new HttpClient())
749+
{
750+
var response = await client.GetAsync(template.TarBallURL);
751+
response.EnsureSuccessStatusCode();
752+
753+
var fileBytes = await response.Content.ReadAsByteArrayAsync();
754+
File.WriteAllBytes(targetFilePath, fileBytes);
755+
756+
// Mark as downloaded
757+
template.IsDownloaded = true;
758+
759+
Tools.SetStatus($"Template downloaded: {template.Name}");
760+
761+
// Refresh no longer needed with INotifyPropertyChanged
762+
// listOnlineTemplates.Items.Refresh();
763+
}
764+
}
765+
catch (Exception ex)
766+
{
767+
Tools.SetStatus($"Download failed: {ex.Message}");
768+
Console.WriteLine($"Error downloading template: {ex.Message}");
769+
}
770+
finally
771+
{
772+
// Re-enable button
773+
button.IsEnabled = true;
774+
button.Content = "⬇";
775+
}
776+
}
777+
}
686778
} // class NewProject
687779
} // namespace UnityLauncherPro

0 commit comments

Comments
 (0)