Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature - Container Queries #16846

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
7022ed1
add container queries
emmauss Aug 28, 2024
584179a
make visual query provider SetSize virtual
emmauss Aug 28, 2024
d88474d
fix container name matching
emmauss Aug 29, 2024
6bd868b
add tests
emmauss Aug 29, 2024
bb68d7b
move border container implementation up to decorator
emmauss Aug 29, 2024
2f310a4
move container query demo to ControlCatalog
emmauss Sep 4, 2024
9d88917
make QueryProvider internal
emmauss Sep 4, 2024
74164b9
update container behavior in toplevelRename Query to StyleQuery and m…
emmauss Sep 4, 2024
b748d37
fix comment typos
emmauss Sep 6, 2024
7be4f47
Merge remote-tracking branch 'origin/master' into feat-container-queries
emmauss Nov 12, 2024
79d4f8f
Merge remote-tracking branch 'origin/master' into feat-container-queries
emmauss Nov 18, 2024
5d4c48d
remove unused usings
emmauss Nov 18, 2024
950ec5a
isolate container tests
emmauss Nov 18, 2024
5cc65f9
Merge branch 'master' into feat-container-queries
emmauss Nov 19, 2024
6da194b
Merge branch 'master' into feat-container-queries
emmauss Nov 25, 2024
e55de92
Merge remote-tracking branch 'origin/master' into feat-container-queries
emmauss Jan 14, 2025
29213a8
update api
emmauss Jan 22, 2025
ca91718
fix tests
emmauss Jan 23, 2025
b941172
fix no-selector styles in containers being applied all the time
emmauss Jan 23, 2025
9dc68ae
simplify container search
emmauss Jan 23, 2025
d8bfb02
Merge branch 'master' into feat-container-queries
emmauss Jan 23, 2025
2a398f1
add docs to container properties
emmauss Jan 27, 2025
c190a3b
Merge branch 'master' into feat-container-queries
emmauss Jan 27, 2025
4f46814
Merge remote-tracking branch 'origin/master' into feat-container-queries
emmauss Feb 3, 2025
a38db7b
Merge branch 'master' into feat-container-queries
emmauss Feb 14, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Avalonia.sln
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tizen", "Tizen", "{D1300000
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Tizen", "src\Tizen\Avalonia.Tizen\Avalonia.Tizen.csproj", "{DFFBDBF5-5DBE-47ED-9EAE-D40B75AC99E8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Tizen", "samples\ControlCatalog.Tizen\ControlCatalog.Tizen.csproj", "{A0B29221-2B6F-4B29-A4D5-2227811B5915}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Tizen", "samples\ControlCatalog.Tizen\ControlCatalog.Tizen.csproj", "{A0B29221-2B6F-4B29-A4D5-2227811B5915}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Metal", "src\Avalonia.Metal\Avalonia.Metal.csproj", "{60B4ED1F-ECFA-453B-8A70-1788261C8355}"
EndProject
Expand Down
Binary file added samples/ControlCatalog/Assets/image1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/ControlCatalog/Assets/image2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/ControlCatalog/Assets/image3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/ControlCatalog/Assets/image4.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/ControlCatalog/Assets/image5.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/ControlCatalog/Assets/image6.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added samples/ControlCatalog/Assets/image7.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions samples/ControlCatalog/ControlCatalog.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,15 @@
<AvaloniaResource Include="Assets\*" />
<AvaloniaResource Include="Assets\Fonts\*" />
</ItemGroup>
<ItemGroup>
<None Remove="Assets\image1.jpg" />
<None Remove="Assets\image2.jpg" />
<None Remove="Assets\image3.jpg" />
<None Remove="Assets\image4.jpg" />
<None Remove="Assets\image5.jpg" />
<None Remove="Assets\image6.jpg" />
<None Remove="Assets\image7.jpg" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Assets\Fonts\SourceSansPro-Bold.ttf" />
<EmbeddedResource Include="Assets\Fonts\SourceSansPro-BoldItalic.ttf" />
Expand Down
3 changes: 3 additions & 0 deletions samples/ControlCatalog/MainView.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@
<TabItem Header="ComboBox">
<pages:ComboBoxPage />
</TabItem>
<TabItem Header="Container Queries">
<pages:ContainerQueryPage />
</TabItem>
<TabItem Header="ContextFlyout">
<pages:ContextFlyoutPage />
</TabItem>
Expand Down
110 changes: 110 additions & 0 deletions samples/ControlCatalog/Pages/ContainerQueryPage.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<UserControl x:Class="ControlCatalog.Pages.ContainerQueryPage"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:viewModels="using:ControlCatalog.ViewModels"
d:DesignHeight="800"
d:DesignWidth="400"
mc:Ignorable="d">
<StackPanel Spacing="10">
<StackPanel.Styles>
<ContainerQuery Name="UniformGrid"
Query="max-width:400">
<Style Selector="UniformGrid#ContentGrid">
<Setter Property="Columns"
Value="1"/>
</Style>
</ContainerQuery>
<ContainerQuery Name="UniformGrid"
Query="min-width:400">
<Style Selector="UniformGrid#ContentGrid">
<Setter Property="Columns"
Value="2"/>
</Style>
</ContainerQuery>
<ContainerQuery Name="UniformGrid"
Query="min-width:800">
<Style Selector="UniformGrid#ContentGrid">
<Setter Property="Columns"
Value="3"/>
</Style>
</ContainerQuery>
<ContainerQuery Name="UniformGrid"
Query="min-width:1200">
<Style Selector="UniformGrid#ContentGrid">
<Setter Property="Columns"
Value="4"/>
</Style>
</ContainerQuery>
</StackPanel.Styles>
<TextBlock Text="Dynamically change properties of controls based on the size of a parent container."/>
<Border Container.Name="UniformGrid"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Container.Sizing="Width">
<ScrollViewer VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled">
<Grid RowDefinitions="Auto,*">
<UniformGrid Name="ContentGrid">
<Border Margin="10"
HorizontalAlignment="Stretch"
CornerRadius="20"
ClipToBounds="True">
<Image Stretch="Uniform"
HorizontalAlignment="Stretch"
Source="/Assets/image1.jpg"/>
</Border>
<Border Margin="10"
HorizontalAlignment="Stretch"
CornerRadius="20"
ClipToBounds="True">
<Image Stretch="Uniform"
HorizontalAlignment="Stretch"
Source="/Assets/image2.jpg"/>
</Border>
<Border Margin="10"
HorizontalAlignment="Stretch"
CornerRadius="20"
ClipToBounds="True">
<Image Stretch="Uniform"
HorizontalAlignment="Stretch"
Source="/Assets/image3.jpg"/>
</Border>
<Border Margin="10"
HorizontalAlignment="Stretch"
CornerRadius="20"
ClipToBounds="True">
<Image Stretch="Uniform"
HorizontalAlignment="Stretch"
Source="/Assets/image4.jpg"/>
</Border>
<Border Margin="10"
HorizontalAlignment="Stretch"
CornerRadius="20"
ClipToBounds="True">
<Image Stretch="Uniform"
HorizontalAlignment="Stretch"
Source="/Assets/image5.jpg"/>
</Border>
<Border Margin="10"
HorizontalAlignment="Stretch"
CornerRadius="20"
ClipToBounds="True">
<Image Stretch="Uniform"
HorizontalAlignment="Stretch"
Source="/Assets/image6.jpg"/>
</Border>
<Border HorizontalAlignment="Stretch"
CornerRadius="20"
ClipToBounds="True">
<Image Stretch="Uniform"
HorizontalAlignment="Stretch"
Source="/Assets/image7.jpg"/>
</Border>
</UniformGrid>
</Grid>
</ScrollViewer>
</Border>
</StackPanel>
</UserControl>
18 changes: 18 additions & 0 deletions samples/ControlCatalog/Pages/ContainerQueryPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;

namespace ControlCatalog.Pages
{
public class ContainerQueryPage : UserControl
{
public ContainerQueryPage()
{
this.InitializeComponent();
}

private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}
28 changes: 28 additions & 0 deletions src/Avalonia.Base/Layout/ContainerSizing.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace Avalonia.Layout
{
/// <summary>
/// Defines how a container is queried.
/// </summary>
public enum ContainerSizing
{
/// <summary>
/// The container is not included in any size queries.
/// </summary>
Normal,

/// <summary>
/// The container size can be queried for width.
/// </summary>
Width,

/// <summary>
/// The container size can be queried for height.
/// </summary>
Height,

/// <summary>
/// The container size can be queried for width and height.
/// </summary>
WidthAndHeight
}
}
47 changes: 43 additions & 4 deletions src/Avalonia.Base/Layout/Layoutable.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using Avalonia.Logging;
using Avalonia.Reactive;
using Avalonia.Styling;
using Avalonia.VisualTree;

#nullable enable
Expand Down Expand Up @@ -543,13 +544,51 @@ protected virtual Size MeasureCore(Size availableSize)
ApplyStyling();
ApplyTemplate();

var constrained = LayoutHelper.ApplyLayoutConstraints(
var constrainedSize = LayoutHelper.ApplyLayoutConstraints(
this,
availableSize.Deflate(margin));
var measured = MeasureOverride(constrained);

var width = measured.Width;
var height = measured.Height;
var isContainer = false;
ContainerSizing containerSizing = ContainerSizing.Normal;

if (Container.GetQueryProvider(this) is { } queryProvider && Container.GetSizing(this) is { } sizing && sizing != ContainerSizing.Normal)
{
isContainer = true;
containerSizing = sizing;
queryProvider.SetSize(constrainedSize.Width, constrainedSize.Height, containerSizing);
}

var measured = MeasureOverride(constrainedSize);

double width, height;

if (isContainer)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe that this logic should be bypassed for TopLevels and TopLevel should always implicitly be a SizeAndHeight container.

{
switch (containerSizing)
{
case ContainerSizing.Width:
width = double.IsInfinity(constrainedSize.Width) ? measured.Width : constrainedSize.Width;
height = measured.Height;
break;
case ContainerSizing.Height:
width = measured.Width;
height = double.IsInfinity(constrainedSize.Height) ? measured.Height : constrainedSize.Height;
break;
case ContainerSizing.WidthAndHeight:
width = double.IsInfinity(constrainedSize.Width) ? measured.Width : constrainedSize.Width;
height = double.IsInfinity(constrainedSize.Height) ? measured.Height : constrainedSize.Height;
break;
default:
width = measured.Width;
height = measured.Height;
break;
}
}
else
{
width = measured.Width;
height = measured.Height;
}

{
double widthCache = Width;
Expand Down
37 changes: 37 additions & 0 deletions src/Avalonia.Base/Platform/VisualQueryProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.Styling;

namespace Avalonia.Platform
{
internal class VisualQueryProvider
{
private readonly Visual _visual;

public double Width { get; private set; } = double.PositiveInfinity;

public double Height { get; private set; } = double.PositiveInfinity;

public VisualQueryProvider(Visual visual)
{
_visual = visual;
}

public event EventHandler? WidthChanged;
public event EventHandler? HeightChanged;

public virtual void SetSize(double width, double height, Layout.ContainerSizing containerType)
{
var currentWidth = Width;
var currentHeight = Height;

Width = width;
Height = height;

if (currentWidth != Width && (containerType == Layout.ContainerSizing.Width || containerType == Layout.ContainerSizing.WidthAndHeight))
WidthChanged?.Invoke(this, EventArgs.Empty);
if (currentHeight != Height && (containerType == Layout.ContainerSizing.Height || containerType == Layout.ContainerSizing.WidthAndHeight))
HeightChanged?.Invoke(this, EventArgs.Empty);
}
}
}
3 changes: 3 additions & 0 deletions src/Avalonia.Base/StyledElement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,9 @@ private void ApplyStyles(IStyleHost host)

private void ApplyStyle(IStyle style, IStyleHost? host, FrameType type)
{
if (style is Styling.ContainerQuery m)
m.TryAttach(this, host, type);

if (style is Style s)
s.TryAttach(this, host, type);

Expand Down
69 changes: 69 additions & 0 deletions src/Avalonia.Base/Styling/Activators/AndQueryActivator.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
using System.Collections.Generic;

namespace Avalonia.Styling.Activators
{
/// <summary>
/// An aggregate <see cref="ContainerQueryActivatorBase"/> which is active when all of its inputs are
/// active.
/// </summary>
internal class AndQueryActivator : ContainerQueryActivatorBase, IStyleActivatorSink
{
private List<IStyleActivator>? _sources;

public AndQueryActivator(Visual visual) : base(visual)
{
}

public int Count => _sources?.Count ?? 0;

public void Add(IStyleActivator activator)
{
if (IsSubscribed)
throw new AvaloniaInternalException("AndActivator is already subscribed.");
_sources ??= new List<IStyleActivator>();
_sources.Add(activator);
}

void IStyleActivatorSink.OnNext(bool value) => ReevaluateIsActive();

protected override bool EvaluateIsActive()
{
if (_sources is null || _sources.Count == 0)
return true;

var count = _sources.Count;
var mask = (1ul << count) - 1;
var flags = 0UL;

for (var i = 0; i < count; ++i)
{
if (_sources[i].GetIsActive())
flags |= 1ul << i;
}

return flags == mask;
}

protected override void Initialize()
{
if (_sources is object)
{
foreach (var source in _sources)
{
source.Subscribe(this);
}
}
}

protected override void Deinitialize()
{
if (_sources is object)
{
foreach (var source in _sources)
{
source.Unsubscribe(this);
}
}
}
}
}
Loading