Skip to content

Commit

Permalink
Make theme thumbnail loading asynchronous
Browse files Browse the repository at this point in the history
  • Loading branch information
t1m0thyj committed Jun 3, 2019
1 parent 2dc4e49 commit 5b1219d
Show file tree
Hide file tree
Showing 15 changed files with 231 additions and 195 deletions.
6 changes: 5 additions & 1 deletion src/DebugLogger.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
using System;
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
Expand Down
13 changes: 11 additions & 2 deletions src/DownloadDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,13 @@ public DownloadDialog()

public void InitDownload(ThemeConfig theme)
{
ThemeManager.downloadMode = true;
this.Invoke(new Action(() =>
label1.Text = string.Format(_("Downloading images for '{0}'..."),
ThemeManager.GetThemeName(theme))));

imagesZipDest = theme.themeId + "_images.zip";
themeUris = ThemeManager.GetThemeUris(theme.themeId);
themeUris = theme.imageUrls.ToList();
themeUriIndex = 0;
DownloadNext(theme);
}
Expand All @@ -65,6 +66,13 @@ private void UpdatePercentage(int percentage)
TaskbarProgress.SetValue(this.Handle, percentage, 100);
}

private bool EnsureZipNotHtml()
{
// Handle case where HTML page gets downloaded instead of ZIP
return (File.Exists(imagesZipDest) &&
((new FileInfo(imagesZipDest)).Length > 1024 * 1024));
}

private void OnDownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
UpdatePercentage(e.ProgressPercentage);
Expand All @@ -82,7 +90,7 @@ public async void OnDownloadFileCompleted(object sender, AsyncCompletedEventArgs
stopwatch.Stop();
ThemeConfig theme = (ThemeConfig)e.UserState;

if (e.Error == null)
if ((e.Error == null) && EnsureZipNotHtml())
{
cancelButton.Enabled = false;
ThemeResult result = await Task.Run(
Expand Down Expand Up @@ -123,6 +131,7 @@ private void cancelButton_Click(object sender, EventArgs e)
private void OnFormClosing(object sender, FormClosingEventArgs e)
{
ThemeLoader.taskbarHandle = IntPtr.Zero;
ThemeManager.downloadMode = false;
wc?.Dispose();

Task.Run(() =>
Expand Down
1 change: 1 addition & 0 deletions src/ImportDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public partial class ImportDialog : Form
private static readonly Func<string, string> _ = Localization.GetTranslation;
private Queue<string> importQueue;
private int numJobs;
public bool thumbnailsLoaded = false;

public ImportDialog()
{
Expand Down
1 change: 1 addition & 0 deletions src/JsonConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ public class AppConfig : INotifyPropertyChanged
public class ThemeConfig
{
public string themeId { get; set; }
public Uri[] imageUrls { get; set; }
public string displayName { get; set; }
public string imageFilename { get; set; }
public string imageCredits { get; set; }
Expand Down
209 changes: 74 additions & 135 deletions src/ThemeDialog.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public ThemeDialog()
this.Font = SystemFonts.MessageBoxFont;
this.FormClosing += OnFormClosing;

int bestWidth = (GetThumbnailSize().Width + 30) * 2 +
int bestWidth = (ThemeThumbLoader.GetThumbnailSize(this).Width + 30) * 2 +
SystemInformation.VerticalScrollBarWidth;
int oldWidth = this.imageListView1.Size.Width;
this.imageListView1.Size = new Size(bestWidth, this.imageListView1.Height);
Expand All @@ -53,101 +53,6 @@ public void ImportThemes(List<string> themePaths)
importDialog.InitImport(themePaths);
}

private Size GetThumbnailSize()
{
int scaledWidth;

using (Graphics g = this.CreateGraphics())
{
scaledWidth = (int)(192 * g.DpiX / 96);
}

return new Size(scaledWidth, scaledWidth * 9 / 16);
}

private Bitmap ShrinkImage(string filename, int width, int height)
{
// Image scaling code from https://stackoverflow.com/a/7677163/5504760
using (var tempImage = Image.FromFile(filename))
{
Bitmap bmp = new Bitmap(width, height);

using (Graphics g = Graphics.FromImage(bmp))
{
g.DrawImage(tempImage, new Rectangle(0, 0, bmp.Width, bmp.Height));
}

return bmp;
}
}

private Image GetThumbnailImage(ThemeConfig theme, Size size, bool useCache = true)
{
string thumbnailPath = Path.Combine("themes", theme.themeId, "thumbnail.png");

if (useCache && File.Exists(thumbnailPath))
{
Image cachedImage = Image.FromFile(thumbnailPath);

if (cachedImage.Size == size)
{
return cachedImage;
}
else
{
cachedImage.Dispose();
File.Delete(thumbnailPath);
}
}
else if (useCache && ThemeManager.defaultThemes.Contains(theme.themeId) &&
!File.Exists(thumbnailPath))
{
return (Image)Properties.Resources.ResourceManager.GetObject(
theme.themeId + "_thumbnail");
}

int imageId1;
int imageId2;

if (theme.dayHighlight.HasValue)
{
imageId1 = theme.dayHighlight.Value;
}
else
{
imageId1 = theme.dayImageList[theme.dayImageList.Length / 2];
}

if (theme.nightHighlight.HasValue)
{
imageId2 = theme.nightHighlight.Value;
}
else
{
imageId2 = theme.nightImageList[theme.nightImageList.Length / 2];
}

string imageFilename1 = theme.imageFilename.Replace("*", imageId1.ToString());
string imageFilename2 = theme.imageFilename.Replace("*", imageId2.ToString());

using (var bmp1 = ShrinkImage(Path.Combine("themes", theme.themeId, imageFilename1),
size.Width, size.Height))
{
Bitmap bmp2 = ShrinkImage(Path.Combine("themes", theme.themeId, imageFilename2),
size.Width, size.Height);

using (Graphics g = Graphics.FromImage(bmp2))
{
g.DrawImage(bmp1, 0, 0, new Rectangle(0, 0, bmp1.Width / 2, bmp1.Height),
GraphicsUnit.Pixel);
}

bmp2.Save(thumbnailPath, System.Drawing.Imaging.ImageFormat.Png);

return bmp2;
}
}

private string GetCreditsText()
{
if (selectedIndex > 0)
Expand Down Expand Up @@ -197,15 +102,16 @@ private void LoadPreviewImage(int imageNumber)

if (selectedIndex == 0)
{
pictureBox1.Image = ShrinkImage(windowsWallpaper, width, height);
pictureBox1.Image = ThemeThumbLoader.ScaleImage(windowsWallpaper,
new Size(width, height));
}
else
{
ThemeConfig theme = ThemeManager.themeSettings[selectedIndex - 1];
int imageId = ThemeManager.GetThemeImageList(theme)[imageNumber - 1];
string imageFilename = theme.imageFilename.Replace("*", imageId.ToString());
pictureBox1.Image = ShrinkImage(Path.Combine("themes", theme.themeId,
imageFilename), width, height);
pictureBox1.Image = ThemeThumbLoader.ScaleImage(Path.Combine("themes",
theme.themeId, imageFilename), new Size(width, height));
}

imageNumberLabel.Text = string.Format(_("Image {0} of {1}"), imageNumber,
Expand All @@ -232,32 +138,48 @@ private void EnsureThemeNotDuplicated(string themeId)
}
}

private void LoadImportedThemes(List<ThemeConfig> themes)
private void LoadImportedThemes(List<ThemeConfig> themes, ImportDialog importDialog)
{
themes.Sort((t1, t2) => t1.themeId.CompareTo(t2.themeId));
Size thumbnailSize = GetThumbnailSize();
Size thumbnailSize = ThemeThumbLoader.GetThumbnailSize(this);
ImageListViewItem newItem = null;

Task.Run(() =>
{
for (int i = 0; i < themes.Count; i++)
{
EnsureThemeNotDuplicated(themes[i].themeId);
this.Invoke(new Action(() => EnsureThemeNotDuplicated(themes[i].themeId)));

string themeName = ThemeManager.GetThemeName(themes[i]);
themeNames.Add(themeName);
themeNames.Sort();
int itemIndex = themeNames.IndexOf(themeName) + 1;

imageListView1.Items.Insert(itemIndex, ThemeManager.GetThemeName(themes[i]),
GetThumbnailImage(themes[i], thumbnailSize, false));
newItem = imageListView1.Items[itemIndex];
newItem.Tag = themes[i].themeId;
using (Image thumbnailImage = ThemeThumbLoader.GetThumbnailImage(themes[i],
thumbnailSize, false))
{
this.Invoke(new Action(() =>
{
imageListView1.Items.Insert(itemIndex,
ThemeManager.GetThemeName(themes[i]), thumbnailImage);
newItem = imageListView1.Items[itemIndex];
newItem.Tag = themes[i].themeId;
}));
}
}

if (newItem != null)
{
newItem.Selected = true;
imageListView1.EnsureVisible(newItem.Index);
this.Invoke(new Action(() =>
{
newItem.Selected = true;
imageListView1.EnsureVisible(newItem.Index);
}));
}

importDialog.thumbnailsLoaded = true;
this.Invoke(new Action(() => importDialog.Close()));
});
}

private void SetThemeDownloaded(bool themeDownloaded)
Expand Down Expand Up @@ -286,7 +208,6 @@ private void UpdateSelectedItem()
t => t.themeId == themeId) + 1;
ThemeConfig theme = ThemeManager.themeSettings[selectedIndex - 1];
themeDownloaded = ThemeManager.IsThemeDownloaded(theme);
SetThemeDownloaded(themeDownloaded);

if (themeDownloaded)
{
Expand All @@ -296,6 +217,7 @@ private void UpdateSelectedItem()
}
}

SetThemeDownloaded(themeDownloaded);
creditsLabel.Text = GetCreditsText();

if (themeDownloaded)
Expand All @@ -317,10 +239,10 @@ private void ThemeDialog_Load(object sender, EventArgs e)
imageListView1.ContextMenuStrip = contextMenuStrip1;
imageListView1.SetRenderer(new ThemeListViewRenderer());

Size thumbnailSize = GetThumbnailSize();
Size thumbnailSize = ThemeThumbLoader.GetThumbnailSize(this);
imageListView1.ThumbnailSize = thumbnailSize;
imageListView1.Items.Add(_("None"), ShrinkImage(windowsWallpaper, thumbnailSize.Width,
thumbnailSize.Height));
imageListView1.Items.Add(_("None"), ThemeThumbLoader.ScaleImage(windowsWallpaper,
thumbnailSize));

string currentTheme = ThemeManager.currentTheme?.themeId;
ImageListViewItem focusItem = null;
Expand All @@ -337,29 +259,38 @@ private void ThemeDialog_Load(object sender, EventArgs e)
}
}

for (int i = 0; i < ThemeManager.themeSettings.Count; i++)
{
ThemeConfig theme = ThemeManager.themeSettings[i];
string themeName = ThemeManager.GetThemeName(theme);
themeNames.Add(themeName);
themeNames.Sort();
Task.Run(new Action(() => {
for (int i = 0; i < ThemeManager.themeSettings.Count; i++)
{
ThemeConfig theme = ThemeManager.themeSettings[i];
string themeName = ThemeManager.GetThemeName(theme);
themeNames.Add(themeName);
themeNames.Sort();
int itemIndex = themeNames.IndexOf(themeName) + 1;

using (Image thumbnailImage = ThemeThumbLoader.GetThumbnailImage(theme,
thumbnailSize, true))
{
this.Invoke(new Action(() => {
imageListView1.Items.Insert(itemIndex, themeName, thumbnailImage);
imageListView1.Items[itemIndex].Tag = theme.themeId;
}));
}

int itemIndex = themeNames.IndexOf(themeName) + 1;
imageListView1.Items.Insert(itemIndex, themeName,
GetThumbnailImage(theme, thumbnailSize));
imageListView1.Items[itemIndex].Tag = theme.themeId;
if (theme.themeId == currentTheme)
{
focusItem = imageListView1.Items[itemIndex];
}
}

if (theme.themeId == currentTheme)
if (focusItem != null)
{
focusItem = imageListView1.Items[itemIndex];
this.Invoke(new Action(() => {
focusItem.Selected = true;
imageListView1.EnsureVisible(focusItem.Index);
}));
}
}

if (focusItem != null)
{
focusItem.Selected = true;
imageListView1.EnsureVisible(focusItem.Index);
}
}));
}

private void imageListView1_SelectionChanged(object sender, EventArgs e)
Expand Down Expand Up @@ -488,15 +419,23 @@ private void OnDownloadDialogClosed(object sender, FormClosedEventArgs e)
UpdateSelectedItem();
}

this.Enabled = true;
this.Enabled = !ThemeManager.importMode;
}

private void OnImportDialogClosing(object sender, FormClosingEventArgs e)
{
LoadImportedThemes(ThemeManager.importedThemes);
ThemeManager.importedThemes.Clear();
ImportDialog importDialog = (ImportDialog)sender;

this.Enabled = true;
if (!importDialog.thumbnailsLoaded)
{
e.Cancel = true;
LoadImportedThemes(ThemeManager.importedThemes, importDialog);
}
else
{
ThemeManager.importedThemes.Clear();
this.Enabled = !ThemeManager.downloadMode;
}
}

private void OnFormClosing(object sender, FormClosingEventArgs e)
Expand Down
Loading

0 comments on commit 5b1219d

Please sign in to comment.