Skip to content

Instantly share code, notes, and snippets.

@LizzyFox-code
Last active April 5, 2024 13:11
Show Gist options
  • Save LizzyFox-code/4158b8e99d198ef4ea5b4c98c74f2979 to your computer and use it in GitHub Desktop.
Save LizzyFox-code/4158b8e99d198ef4ea5b4c98c74f2979 to your computer and use it in GitHub Desktop.
Content scaler for Unity Noesis GUI
#if UNITY_5_3_OR_NEWER
#define NOESIS
using Noesis;
#else
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
#endif
namespace NoesisGUI.Extensions.Scaling
{
using System;
public enum ContentMatchMode
{
MatchWidthOrHeight,
Expand,
Shrink
}
[TemplatePart(Name = "ContentPresenter", Type = typeof(ContentPresenter))]
public class ContentScaler : ContentControl
{
private const float LogBase = 2;
public static readonly DependencyProperty ReferenceWidthProperty = DependencyProperty.Register(nameof(ReferenceWidth),
typeof(float), typeof(ContentScaler), new FrameworkPropertyMetadata(1920.0f, FrameworkPropertyMetadataOptions.AffectsMeasure));
public static readonly DependencyProperty ReferenceHeightProperty = DependencyProperty.Register(nameof(ReferenceHeight),
typeof(float), typeof(ContentScaler), new FrameworkPropertyMetadata(1080.0f, FrameworkPropertyMetadataOptions.AffectsMeasure));
public static readonly DependencyProperty ContentMatchModeProperty = DependencyProperty.Register(nameof(ContentMatchMode),
typeof(ContentMatchMode), typeof(ContentScaler), new FrameworkPropertyMetadata(ContentMatchMode.MatchWidthOrHeight, FrameworkPropertyMetadataOptions.AffectsMeasure));
public static readonly DependencyProperty MatchWidthOrHeightProperty = DependencyProperty.Register(nameof(MatchWidthOrHeight),
typeof(float), typeof(ContentScaler), new FrameworkPropertyMetadata(0.0f, FrameworkPropertyMetadataOptions.AffectsMeasure));
private ContentPresenter m_ContentPresenter;
private ScaleTransform m_ScaleTransform;
public float ReferenceWidth
{
get => (float) GetValue(ReferenceWidthProperty);
set => SetValue(ReferenceWidthProperty, value);
}
public float ReferenceHeight
{
get => (float) GetValue(ReferenceHeightProperty);
set => SetValue(ReferenceHeightProperty, value);
}
public ContentMatchMode ContentMatchMode
{
get => (ContentMatchMode) GetValue(ContentMatchModeProperty);
set => SetValue(ContentMatchModeProperty, value);
}
public float MatchWidthOrHeight
{
get => (float) GetValue(MatchWidthOrHeightProperty);
set => SetValue(MatchWidthOrHeightProperty, value);
}
static ContentScaler()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ContentScaler), new FrameworkPropertyMetadata(typeof(ContentScaler)));
}
public override void OnApplyTemplate()
{
var templateRoot = VisualTreeHelper.GetChild(this, 0) as FrameworkElement;
if(templateRoot == null)
return;
m_ContentPresenter = (ContentPresenter)templateRoot.FindName("ContentPresenter");
m_ScaleTransform = new ScaleTransform(1f, 1f);
m_ContentPresenter.LayoutTransform = m_ScaleTransform;
m_ContentPresenter.Loaded += (sender, args) =>
{
InvalidateMeasure();
};
}
protected override Size MeasureOverride(Size availableSize)
{
if(double.IsPositiveInfinity(availableSize.Width))
availableSize = new Size(ReferenceWidth, availableSize.Height);
if(double.IsPositiveInfinity(availableSize.Height))
availableSize = new Size(availableSize.Width, ReferenceHeight);
var scale = CalculateScale(availableSize);
m_ScaleTransform.ScaleX = scale;
m_ScaleTransform.ScaleY = scale;
return availableSize;
}
private float CalculateScale(Size availableSize)
{
switch (ContentMatchMode)
{
case ContentMatchMode.MatchWidthOrHeight:
{
var logWidth = (float) Math.Log(availableSize.Width / ReferenceWidth, LogBase);
var logHeight = (float) Math.Log(availableSize.Height / ReferenceHeight, LogBase);
var logWeightedAverage = logWidth + (logHeight - logWidth) * MatchWidthOrHeight;
return (float)Math.Pow(LogBase, logWeightedAverage);
}
case ContentMatchMode.Expand:
return (float)Math.Min(availableSize.Width / ReferenceWidth, availableSize.Height / ReferenceHeight);
case ContentMatchMode.Shrink:
return (float)Math.Max(availableSize.Width / ReferenceWidth, availableSize.Height / ReferenceHeight);
default:
return 1.0f;
}
}
}
}
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:scaling="clr-namespace:NoesisGUI.Extensions.Scaling">
<Style TargetType="{x:Type scaling:ContentScaler}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type scaling:ContentScaler}">
<Border>
<ContentPresenter x:Name="ContentPresenter"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
@LizzyFox-code
Copy link
Author

LizzyFox-code commented Nov 18, 2023

How to use:

<UserControl x:Class="Testing.TestScreen"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:scaling="clr-namespace:NoesisGUI.Extensions.Scaling">
    <scaling:ContentScaler ReferenceWidth="1920" ReferenceHeight="1080" ContentMatchMode="MatchWidthOrHeight" MatchWidthOrHeight="0">
        <Grid>
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="64"/>
                <RowDefinition Height="64"/>
            </Grid.RowDefinitions>

        </Grid>
    </scaling:ContentScaler>
</UserControl>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment