Skip to content

Commit

Permalink
Merge pull request #568 from enisn/grid-layout
Browse files Browse the repository at this point in the history
Introduce GridLayout
  • Loading branch information
enisn authored Feb 9, 2024
2 parents 48719b0 + 2822a71 commit e3d0f63
Show file tree
Hide file tree
Showing 10 changed files with 352 additions and 0 deletions.
4 changes: 4 additions & 0 deletions demo/UraniumApp/AppShell.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
<FlyoutItem Title="ExpanderView" Icon="{FontImageSource Glyph={x:Static m:MaterialOutlined.Arrow_drop_down}, FontFamily=MaterialOutlined, Color={AppThemeBinding {StaticResource Primary}, Dark={StaticResource PrimaryDark}}}">
<ShellContent ContentTemplate="{DataTemplate pages:ExpanderViewPage}"/>
</FlyoutItem>

<FlyoutItem Title="GridLayout" Icon="{FontImageSource Glyph={x:Static m:MaterialOutlined.Grid_3x3}, FontFamily=MaterialOutlined, Color={AppThemeBinding {StaticResource Primary}, Dark={StaticResource PrimaryDark}}}">
<ShellContent ContentTemplate="{DataTemplate pages:GridLayoutPage}"/>
</FlyoutItem>

<FlyoutItem Title="Buttons" Icon="{FontImageSource Glyph={x:Static m:MaterialOutlined.All_inbox}, FontFamily=MaterialOutlined, Color={AppThemeBinding {StaticResource Primary}, Dark={StaticResource PrimaryDark}}}">
<ShellContent ContentTemplate="{DataTemplate pages:ButtonsPage}"/>
Expand Down
102 changes: 102 additions & 0 deletions demo/UraniumApp/Pages/GridLayoutPage.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="UraniumApp.Pages.GridLayoutPage"
xmlns:m="clr-namespace:UraniumUI.Icons.MaterialSymbols;assembly=UraniumUI.Icons.MaterialSymbols"
xmlns:uranium="http://schemas.enisn-projects.io/dotnet/maui/uraniumui"
xmlns:material="http://schemas.enisn-projects.io/dotnet/maui/uraniumui/material"
Title="GridLayoutPage">
<ContentPage.Resources>

<x:String x:Key="DocLink">https://enisn-projects.io/docs/en/uranium/latest/themes/material/components/TextField</x:String>
<x:String x:Key="SourceCode">
<![CDATA[
<uranium:GridLayout>
<BoxView Color="Blue" />
<BoxView Color="Green" />
<BoxView Color="Yellow" />
<BoxView Color="Red" />
</uranium:GridLayout>
]]>
</x:String>
<x:String x:Key="SourceCode2">
<![CDATA[
<uranium:GridLayout RowCount="3" ColumnCount="3" ColumnGridLength="*" RowGridLength="120" ColumnSpacing="16" RowSpacing="16">
<BoxView Color="DarkBlue" />
<BoxView Color="Blue" />
<BoxView Color="LightBlue" />
<BoxView Color="Green" />
<BoxView Color="Yellow" />
<BoxView Color="Orange" />
<BoxView Color="Red" />
<BoxView Color="Pink" />
</uranium:GridLayout>
]]>
</x:String>
</ContentPage.Resources>
<ScrollView>
<VerticalStackLayout Padding="20">

<Label Text="GridLayout" FontSize="Title" />
<Label Text="UraniumUI.Layouts" FontSize="Micro" Opacity=".6" />

<HorizontalStackLayout Padding="0,20">
<Button Text="Documentation" Command="{x:Static uranium:Commands.OpenLinkCommand}" CommandParameter="{StaticResource DocLink}" StyleClass="OutlinedButton" ImageSource="{FontImageSource Glyph={x:Static m:MaterialSharp.File_open}, FontFamily=MaterialSharp, Color={AppThemeBinding {StaticResource OnBackground}, Dark={StaticResource OnBackgroundDark}}}" />
<Button Text="Source" StyleClass="OutlinedButton" IsEnabled="False" ImageSource="{FontImageSource Glyph={x:Static m:MaterialSharp.Code}, FontFamily=MaterialSharp, Color={AppThemeBinding {StaticResource OnBackground}, Dark={StaticResource OnBackgroundDark}}}" />
</HorizontalStackLayout>

<Label Text="GridLayout is a variation of MAUI Grid with a set of features. You can build a perfect grid without dealing with Grid.Row and Grid.Column properties. You can also bind dynamic items to the grid." />

<Border StyleClass="SurfaceContainer,Rounded">
<VerticalStackLayout>
<uranium:GridLayout>
<uranium:GridLayout.Resources>
<Style TargetType="BoxView">
<Setter Property="MinimumWidthRequest" Value="120" />
<Setter Property="MinimumHeightRequest" Value="120" />
</Style>
</uranium:GridLayout.Resources>
<BoxView Color="Blue" />
<BoxView Color="Green" />
<BoxView Color="Yellow" />
<BoxView Color="Red" />
</uranium:GridLayout>

<uranium:ExpanderView>
<uranium:ExpanderView.Header>
<Label Text="Source Code (XAML)" Margin="10" />
</uranium:ExpanderView.Header>
<uranium:CodeView HeightRequest="120" SourceCode="{StaticResource SourceCode}"/>
</uranium:ExpanderView>
</VerticalStackLayout>
</Border>

<BoxView StyleClass="Divider" />

<Border StyleClass="SurfaceContainer,Rounded">
<VerticalStackLayout>
<uranium:GridLayout RowCount="3" ColumnCount="3" ColumnGridLength="*" RowGridLength="120" ColumnSpacing="16" RowSpacing="16">
<BoxView Color="DarkBlue" />
<BoxView Color="Blue" />
<BoxView Color="LightBlue" />
<BoxView Color="Green" />
<BoxView Color="Yellow" />
<BoxView Color="Orange" />
<BoxView Color="Red" />
<BoxView Color="Pink" />
</uranium:GridLayout>

<uranium:ExpanderView>
<uranium:ExpanderView.Header>
<Label Text="Source Code (XAML)" Margin="10" />
</uranium:ExpanderView.Header>
<uranium:CodeView HeightRequest="120" SourceCode="{StaticResource SourceCode2}"/>
</uranium:ExpanderView>
</VerticalStackLayout>
</Border>

<BoxView StyleClass="Divider" />

</VerticalStackLayout>
</ScrollView>
</ContentPage>
9 changes: 9 additions & 0 deletions demo/UraniumApp/Pages/GridLayoutPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace UraniumApp.Pages;

public partial class GridLayoutPage : ContentPage
{
public GridLayoutPage()
{
InitializeComponent();
}
}
3 changes: 3 additions & 0 deletions demo/UraniumApp/Pages/InputFields/TextFieldPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
Title="{Binding Title}">

<ContentPage.Resources>

<x:String x:Key="SourceCode">
<![CDATA[
<VerticalStackLayout Spacing="12">
Expand Down Expand Up @@ -46,6 +47,8 @@
</x:String>

<x:String x:Key="DocLink">https://enisn-projects.io/docs/en/uranium/latest/themes/material/components/TextField</x:String>


</ContentPage.Resources>

<ScrollView>
Expand Down
4 changes: 4 additions & 0 deletions docs/en/docs-nav.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@
"text": "Stateful Content View",
"path": "infrastructure/StatefulContentView.md"
},
{
"text": "Grid Layout",
"path": "infrastructure/GridLayout.md"
},
{
"text": "ExpanderView",
"path": "infrastructure/ExpanderView.md"
Expand Down
78 changes: 78 additions & 0 deletions docs/en/infrastructure/GridLayout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# GridLayout

The `GridLayout` is a layout that arranges its children in a grid. It is similar to the MAUI Grid, but it manages the layout of its children automatically. The `GridLayout` is a good choice when you want to create a grid of items that can be manages dynamically without defaling with **Grid.Row** and **Grid.Column** properties for each item. Each item you place inside GridLayout will be automatically placed in the next cell of the grid.


## Usage

It is defined in `UraniumUI.Layouts` namespace. You can use it in XAML like this:

```xml
xmlns:uranium="http://schemas.enisn-projects.io/dotnet/maui/uraniumui"
```

Then you can use it with `uranium:GridLayout` tag.

```xml
<uranium:GridLayout>
<BoxView Color="Blue" />
<BoxView Color="Green" />
<BoxView Color="Yellow" />
<BoxView Color="Red" />
</uranium:GridLayout>
```

The above code will create a grid with 2 columns and 2 rows. The first BoxView will be placed in the first cell, the second BoxView will be placed in the second cell, the third BoxView will be placed in the third cell, and the fourth BoxView will be placed in the fourth cell.


## Properties

- **ColumnCount**: The number of columns in the grid. Default value is 2.
- **RowCount**: The number of rows in the grid. Default value is 2.
- **RowSpacing**: The spacing between rows. Default value is 0.
- **ColumnSpacing**: The spacing between columns. Default value is 0.
- **RowGridLength**: The height of each row. Default value is Auto.
- **ColumnGridLength**: The width of each column. Default value is Auto.



```xml
<uranium:GridLayout Margin="20"
RowCount="2"
ColumnCount="2"
ColumnSpacing="16"
RowSpacing="16"
ColumnGridLength="120"
RowGridLength="120">
<BoxView Color="Red" />
<BoxView Color="Green" />
<BoxView Color="Blue" />
<BoxView Color="Yellow" />
</uranium:GridLayout>
```

![MAUI GridLayout](images/gridlayout-simple.png)


## Span

You can use the **Grid.RowSpan** and **Grid.ColumnSpan** properties to span an item across multiple rows or columns.

```xml
<uranium:GridLayout Margin="20"
RowCount="3"
ColumnCount="3"
ColumnSpacing="16"
RowSpacing="16"
ColumnGridLength="120"
RowGridLength="120">
<BoxView Color="Red" Grid.RowSpan="2" />
<BoxView Color="Green" Grid.ColumnSpan="2" />
<BoxView Color="LightBlue" />
<BoxView Color="Blue" />
<BoxView Color="DarkBlue" />
<BoxView Color="Yellow" />
</uranium:GridLayout>
```

![MAUI GridLayout Span](images/gridlayout-spans.png)
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/UraniumUI/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using UraniumUI;

[assembly: XmlnsDefinition(Constants.XamlNamespace, Constants.NamespacePrefix + nameof(UraniumUI.Controls))]
[assembly: XmlnsDefinition(Constants.XamlNamespace, Constants.NamespacePrefix + nameof(UraniumUI.Layouts))]
[assembly: XmlnsDefinition(Constants.XamlNamespace, Constants.NamespacePrefix + nameof(UraniumUI.Pages))]
[assembly: XmlnsDefinition(Constants.XamlNamespace, Constants.NamespacePrefix + nameof(UraniumUI.Resources))]
[assembly: XmlnsDefinition(Constants.XamlNamespace, Constants.NamespacePrefix + nameof(UraniumUI.Theming))]
Expand Down
151 changes: 151 additions & 0 deletions src/UraniumUI/Layouts/GridLayout.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
using System.ComponentModel;
using UraniumUI.Extensions;

namespace UraniumUI.Layouts;
public class GridLayout : Grid
{
public int RowCount { get => (int)GetValue(RowCountProperty); set => SetValue(RowCountProperty, value); }

public static readonly BindableProperty RowCountProperty = BindableProperty.Create(
nameof(RowCount), typeof(int), typeof(GridLayout),
defaultValue: 2,
propertyChanged: (bindable, oldValue, newValue) =>
{
if (bindable is GridLayout grid)
{
grid.SetRowDefinitions();
}
});

public int ColumnCount { get => (int)GetValue(ColumnCountProperty); set => SetValue(ColumnCountProperty, value); }

public static readonly BindableProperty ColumnCountProperty = BindableProperty.Create(
nameof(ColumnCount), typeof(int), typeof(GridLayout),
defaultValue: 2,
propertyChanged: (bindable, oldValue, newValue) =>
{
if (bindable is GridLayout grid)
{
grid.SetColumnDefinitions();
}
});

[TypeConverter(typeof(GridLengthTypeConverter))]
public GridLength ColumnGridLength { get => (GridLength)GetValue(ColumnGridLengthProperty); set => SetValue(ColumnGridLengthProperty, value); }

public static readonly BindableProperty ColumnGridLengthProperty = BindableProperty.Create(
nameof(ColumnGridLength), typeof(GridLength), typeof(GridLayout), GridLength.Auto,
propertyChanged: (bindable, oldValue, newValue) =>
{
if (bindable is GridLayout grid)
{
grid.SetColumnDefinitions();
}
});

[TypeConverter(typeof(GridLengthTypeConverter))]
public GridLength RowGridLength { get => (GridLength)GetValue(RowGridLengthProperty); set => SetValue(RowGridLengthProperty, value); }

public static readonly BindableProperty RowGridLengthProperty = BindableProperty.Create(
nameof(RowGridLength), typeof(GridLength), typeof(GridLayout), GridLength.Auto,
propertyChanged: (bindable, oldValue, newValue) =>
{
if (bindable is GridLayout grid)
{
grid.SetRowDefinitions();
}
});

protected override void OnParentSet()
{
base.OnParentSet();
SetDefinitions();
}

protected override void OnChildAdded(Element child)
{
base.OnChildAdded(child);

var view = child as View;
//this.Children.Remove(view);

var (row, column) = GetNextAvailablePosition();

Grid.SetRow(view, row);
Grid.SetColumn(view, column);
//this.Add(view);
}

protected virtual void SetDefinitions()
{
SetColumnDefinitions();
SetRowDefinitions();
}

protected virtual void SetColumnDefinitions()
{
ColumnDefinitions.Clear();
for (int i = 0; i < ColumnCount; i++)
{
ColumnDefinitions.Add(new ColumnDefinition(ColumnGridLength));
}
}

protected virtual void SetRowDefinitions()
{
RowDefinitions.Clear();
for (int i = 0; i < RowCount; i++)
{
RowDefinitions.Add(new RowDefinition(RowGridLength));
}
}

private (int, int) GetNextAvailablePosition()
{
for (int row = 0; row < RowCount; row++)
{
for (int column = 0; column < ColumnCount; column++)
{
if (IsCellAvailable(row, column))
{
return (row, column);
}
}
}

return (0, 0);
}

private bool IsCellAvailable(int row, int column)
{
foreach (var child in Children.Take(Children.Count - 1))
{
if (child is View view)
{
int childRow = (int)view.GetValue(RowProperty);
int childColumn = (int)view.GetValue(ColumnProperty);
int childRowSpan = (int)view.GetValue(RowSpanProperty);
int childColumnSpan = (int)view.GetValue(ColumnSpanProperty);

if (row >= childRow && row < childRow + childRowSpan &&
column >= childColumn && column < childColumn + childColumnSpan)
{
return false;
}
}
}

return true;
}

private static void FindEndPoint(View view, out int row, out int column)
{
row = (int)view.GetValue(RowProperty);
column = (int)view.GetValue(ColumnProperty);
int rowSpan = (int)view.GetValue(RowSpanProperty);
int columnSpan = (int)view.GetValue(ColumnSpanProperty);

row += rowSpan;
column += columnSpan;
}
}

0 comments on commit e3d0f63

Please sign in to comment.