Skip to content

Commit

Permalink
Improved albums rendering logic (thx Peter <3)
Browse files Browse the repository at this point in the history
  • Loading branch information
FrayxRulez committed Jun 24, 2020
1 parent 6996651 commit bbbea31
Show file tree
Hide file tree
Showing 14 changed files with 662 additions and 1,294 deletions.
453 changes: 453 additions & 0 deletions Unigram/Unigram/Common/AlbumLayout.cs

Large diffs are not rendered by default.

591 changes: 0 additions & 591 deletions Unigram/Unigram/Common/GroupedMessages.cs

This file was deleted.

104 changes: 101 additions & 3 deletions Unigram/Unigram/Common/TdExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1925,7 +1925,7 @@ public static bool UpdateFile(this MessageInvoice invoice, File file)
public static bool UpdateFile(this MessageAlbum album, File file)
{
var any = false;
foreach (var message in album.Layout.Messages)
foreach (var message in album.Messages)
{
if (message.UpdateFile(file))
{
Expand Down Expand Up @@ -2177,17 +2177,115 @@ public class MessageAlbum : MessageContent
{
public FormattedText Caption { get; set; }

public GroupedMessages Layout { get; private set; }
public UniqueList<long, MessageViewModel> Messages { get; } = new UniqueList<long, MessageViewModel>(x => x.Id);

public MessageAlbum()
{
Layout = new GroupedMessages();
}

public NativeObject ToUnmanaged()
{
throw new NotImplementedException();
}

public const double ITEM_MARGIN = 2;
public const double MAX_WIDTH = 320 + ITEM_MARGIN;
public const double MAX_HEIGHT = 420 + ITEM_MARGIN;

private ((Rect, MosaicItemPosition)[], Size)? _positions;

public void Invalidate()
{
_positions = null;
}

public (Rect[], Size) GetPositionsForWidth(double w)
{
var positions = _positions ??= MosaicAlbumLayout.chatMessageBubbleMosaicLayout(new Size(MAX_WIDTH, MAX_HEIGHT), GetSizes());

var ratio = w / positions.Item2.Width;
var rects = new Rect[positions.Item1.Length];

for (int i = 0; i < rects.Length; i++)
{
var rect = positions.Item1[i].Item1;
rects[i] = new Rect(rect.X * ratio, rect.Y * ratio, rect.Width * ratio, rect.Height * ratio);
}

return (rects, new Size(positions.Item2.Width * ratio, positions.Item2.Height * ratio));
}

private IEnumerable<Size> GetSizes()
{
foreach (var message in Messages)
{
if (message.Content is MessagePhoto photoMedia)
{
yield return GetClosestPhotoSizeWithSize(photoMedia.Photo.Sizes, 1280, false);
}
else if (message.Content is MessageVideo videoMedia)
{
if (videoMedia.Video.Width != 0 && videoMedia.Video.Height != 0)
{
yield return new Size(videoMedia.Video.Width, videoMedia.Video.Height);
}
else if (videoMedia.Video.Thumbnail != null)
{
yield return new Size(videoMedia.Video.Thumbnail.Width, videoMedia.Video.Thumbnail.Height);
}
else
{
yield return Size.Empty;
}
}
}
}

public static Size GetClosestPhotoSizeWithSize(IList<PhotoSize> sizes, int side)
{
return GetClosestPhotoSizeWithSize(sizes, side, false);
}

public static Size GetClosestPhotoSizeWithSize(IList<PhotoSize> sizes, int side, bool byMinSide)
{
if (sizes == null || sizes.IsEmpty())
{
return Size.Empty;
}
int lastSide = 0;
PhotoSize closestObject = null;
for (int a = 0; a < sizes.Count; a++)
{
PhotoSize obj = sizes[a];
if (obj == null)
{
continue;
}

int w = obj.Width;
int h = obj.Height;

if (byMinSide)
{
int currentSide = h >= w ? w : h;
if (closestObject == null || side > 100 && side > lastSide && lastSide < currentSide)
{
closestObject = obj;
lastSide = currentSide;
}
}
else
{
int currentSide = w >= h ? w : h;
if (closestObject == null || side > 100 && currentSide <= side && lastSide < currentSide)
{
closestObject = obj;
lastSide = currentSide;
}
}
}
return new Size(closestObject.Width, closestObject.Height);
}
}

public class MessageChatEvent : MessageContent
Expand Down
4 changes: 2 additions & 2 deletions Unigram/Unigram/Controls/AspectView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ protected override Size MeasureOverride(Size availableSize)
ttl = viewModel.Ttl > 0;
constraint = viewModel.Content;

if (viewModel.MediaAlbumId != 0 && Tag is GroupedMessagePosition)
if (viewModel.MediaAlbumId != 0 && Tag is true)
{
return base.MeasureOverride(availableSize);
}
Expand All @@ -60,7 +60,7 @@ protected override Size MeasureOverride(Size availableSize)
ttl = message.Ttl > 0;
constraint = message.Content;

if (message.MediaAlbumId != 0 && Tag is GroupedMessagePosition)
if (message.MediaAlbumId != 0 && Tag is true)
{
return base.MeasureOverride(availableSize);
}
Expand Down
146 changes: 29 additions & 117 deletions Unigram/Unigram/Controls/Messages/Content/AlbumContent.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Telegram.Td.Api;
using Unigram.Common;
using Unigram.ViewModels;
using Windows.Foundation;
using Windows.UI.Xaml;
Expand All @@ -15,133 +13,50 @@ public sealed partial class AlbumContent : Grid, IContentWithFile
public MessageViewModel Message => _message;
private MessageViewModel _message;

public const double ITEM_MARGIN = 2;
public const double MAX_WIDTH = 320 + ITEM_MARGIN;
public const double MAX_HEIGHT = 420 + ITEM_MARGIN;

public AlbumContent(MessageViewModel message)
{
InitializeComponent();
UpdateMessage(message);

// I don't like this much, but it's the easier way to add margins between children
Margin = new Thickness(0, 0, -2, -2);
Margin = new Thickness(0, 0, -MessageAlbum.ITEM_MARGIN, -MessageAlbum.ITEM_MARGIN);
}

private (Rect[], Size) _positions;

protected override Size MeasureOverride(Size availableSize)
{
var album = _message.Content as MessageAlbum;
if (album == null)
if (album == null || album.Messages.Count == 1)
{
return base.MeasureOverride(availableSize);
}

var groupedMessages = album.Layout;
if (groupedMessages.Messages.Count == 1)
{
return base.MeasureOverride(availableSize);
}

var positions = groupedMessages.Positions.ToList();

var groupedWidth = (double)groupedMessages.Width;
//var width = groupedMessages.Width / 800d * Math.Min(availableSize.Width, MAX_WIDTH);
var width = availableSize.Width;
var height = width / MAX_WIDTH * MAX_HEIGHT;
//var height = groupedMessages.Width / 800d * MAX_HEIGHT;

var size = new Size(width, groupedMessages.Height * height);
var positions = album.GetPositionsForWidth(availableSize.Width);

for (int i = 0; i < Math.Min(positions.Count, Children.Count); i++)
for (int i = 0; i < Math.Min(positions.Item1.Length, Children.Count); i++)
{
Children[i].Measure(new Size(positions[i].Value.Width / groupedWidth * width, height * positions[i].Value.Height));
Children[i].Measure(new Size(positions.Item1[i].Width, positions.Item1[i].Height));
}

return size;
_positions = positions;
return positions.Item2;
}

protected override Size ArrangeOverride(Size finalSize)
{
var album = _message.Content as MessageAlbum;
if (album == null)
var positions = _positions;
if (positions.Item1 == null || positions.Item1.Length == 1)
{
return base.ArrangeOverride(finalSize);
}

var groupedMessages = album.Layout;
if (groupedMessages.Messages.Count == 1)
for (int i = 0; i < Math.Min(positions.Item1.Length, Children.Count); i++)
{
return base.ArrangeOverride(finalSize);
Children[i].Arrange(positions.Item1[i]);
}

var positions = groupedMessages.Positions.ToList();

var groupedWidth = (double)groupedMessages.Width;
var width = finalSize.Width;
var height = width / MAX_WIDTH * MAX_HEIGHT;
//var height = groupedMessages.Width / 800d * MAX_HEIGHT;

var size = new Size(width, groupedMessages.Height * height);

var total = 0d;
var space = 0d;

for (int i = 0; i < Math.Min(positions.Count, Children.Count); i++)
{
var position = positions[i];

var top = total;
var left = 0d;

if (i > 0)
{
var pos = positions[i - 1];
// in one row
if (pos.Value.MinY == position.Value.MinY)
{
for (var j = i - 1; j >= 0; j--)
{
pos = positions[j];
if (pos.Value.MinY == position.Value.MinY)
{
left += pos.Value.Width / groupedWidth * width;
}
}
}
// in one column
else if (position.Value.SpanSize == groupedMessages.MaxSizeWidth || position.Value.SpanSize == 1000)
{
left = position.Value.LeftSpanOffset / groupedWidth * width;
// find common big message
KeyValuePair<MessageViewModel, GroupedMessagePosition>? leftColumn = null;
for (var j = i - 1; j >= 0; j--)
{
pos = positions[j];
if (pos.Value.SiblingHeights != null)
{
leftColumn = pos;
break;
}
else if (pos.Value.LeftSpanOffset > 0)
{
top += height * pos.Value.Height;
}
}
}
}

space += positions[i].Value.Width;

if (space >= groupedMessages.Width)
{
space = 0;
total += height * position.Value.Height;
}

Children[i].Arrange(new Rect(left, top, positions[i].Value.Width / groupedWidth * width, height * positions[i].Value.Height));
}

return size;
return positions.Item2;
}

public void UpdateMessage(MessageViewModel message)
Expand All @@ -156,43 +71,40 @@ public void UpdateMessage(MessageViewModel message)

Children.Clear();

var groupedMessages = album.Layout;
if (groupedMessages.Messages.Count == 1)
if (album.Messages.Count == 1)
{
if (groupedMessages.Messages[0].Content is MessagePhoto)
if (album.Messages[0].Content is MessagePhoto)
{
Children.Add(new PhotoContent(groupedMessages.Messages[0]));
Children.Add(new PhotoContent(album.Messages[0]));
}
else if (groupedMessages.Messages[0].Content is MessageVideo)
else if (album.Messages[0].Content is MessageVideo)
{
Children.Add(new VideoContent(groupedMessages.Messages[0]));
Children.Add(new VideoContent(album.Messages[0]));
}

return;
}

var positions = groupedMessages.Positions.ToList();

foreach (var pos in positions)
foreach (var pos in album.Messages)
{
AspectView element = null;
if (pos.Key.Content is MessagePhoto)
if (pos.Content is MessagePhoto)
{
element = new PhotoContent(pos.Key);
element = new PhotoContent(pos);
}
else if (pos.Key.Content is MessageVideo)
else if (pos.Content is MessageVideo)
{
element = new VideoContent(pos.Key);
element = new VideoContent(pos);
}

if (element != null)
{
element.MinWidth = 0;
element.MinHeight = 0;
element.MaxWidth = MAX_WIDTH;
element.MaxHeight = MAX_HEIGHT;
element.BorderThickness = new Thickness(0, 0, ITEM_MARGIN, ITEM_MARGIN);
element.Tag = pos.Value;
element.MaxWidth = MessageAlbum.MAX_WIDTH;
element.MaxHeight = MessageAlbum.MAX_HEIGHT;
element.BorderThickness = new Thickness(0, 0, MessageAlbum.ITEM_MARGIN, MessageAlbum.ITEM_MARGIN);
element.Tag = true;

Children.Add(element);
}
Expand All @@ -215,7 +127,7 @@ public void UpdateFile(MessageViewModel message, File file)
{
if (child is IContentWithFile content)
{
var media = album.Layout.Messages.FirstOrDefault(x => x.Id == content.Message?.Id);
var media = album.Messages.FirstOrDefault(x => x.Id == content.Message?.Id);
if (media != null)
{
content.UpdateFile(media, file);
Expand Down
14 changes: 6 additions & 8 deletions Unigram/Unigram/Controls/Messages/MessageBubble.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1688,24 +1688,22 @@ protected override Size MeasureOverride(Size availableSize)
}
else if (constraint is MessageAlbum album)
{
var groupedMessages = album.Layout;
if (groupedMessages.Messages.Count == 1)
if (album.Messages.Count == 1)
{
if (groupedMessages.Messages[0].Content is MessagePhoto photoContent)
if (album.Messages[0].Content is MessagePhoto photoContent)
{
constraint = photoContent.Photo;
}
else if (groupedMessages.Messages[0].Content is MessageVideo videoContent)
else if (album.Messages[0].Content is MessageVideo videoContent)
{
constraint = videoContent.Video;
}
}
else
{

width = groupedMessages.Width / 800d * Math.Min(availableSize.Width, AlbumContent.MAX_WIDTH - AlbumContent.ITEM_MARGIN);
height = width / (AlbumContent.MAX_WIDTH - AlbumContent.ITEM_MARGIN) * (AlbumContent.MAX_HEIGHT - AlbumContent.ITEM_MARGIN);
height = groupedMessages.Height * height;
var positions = album.GetPositionsForWidth(availableWidth + MessageAlbum.ITEM_MARGIN);
width = positions.Item2.Width - MessageAlbum.ITEM_MARGIN;
height = positions.Item2.Height;

goto Calculate;
}
Expand Down
Loading

0 comments on commit bbbea31

Please sign in to comment.