Skip to content

Commit

Permalink
Merge pull request #247 from fabulous-dev/items-control
Browse files Browse the repository at this point in the history
ItemsControl Widget
  • Loading branch information
edgarfgp authored May 17, 2024
2 parents 29d2cb0 + 75bfda5 commit a256fb4
Show file tree
Hide file tree
Showing 11 changed files with 147 additions and 34 deletions.
3 changes: 3 additions & 0 deletions samples/Gallery/App.fs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ module App =
| GridSplitterPage
| ImagePage
| ItemsRepeaterPage
| ItemsControlPage
| LabelPage
| LayoutTransformControlPage
| ListBoxPage
Expand Down Expand Up @@ -117,6 +118,7 @@ module App =
| "GridSplitter" -> ValueSome GridSplitterPage
| "Image" -> ValueSome ImagePage
| "ItemsRepeater" -> ValueSome ItemsRepeaterPage
| "ItemsControl" -> ValueSome ItemsControlPage
| "Label" -> ValueSome LabelPage
| "LayoutTransformControl" -> ValueSome LayoutTransformControlPage
| "ListBox" -> ValueSome ListBoxPage
Expand Down Expand Up @@ -195,6 +197,7 @@ module App =
| GridSplitterPage -> ValueSome(AnyView(GridSplitterPage.view()))
| ImagePage -> ValueSome(AnyView(ImagePage.view()))
| ItemsRepeaterPage -> ValueSome(AnyView(ItemsRepeaterPage.view()))
| ItemsControlPage -> ValueSome(AnyView(ItemsControlPage.view()))
| LabelPage -> ValueSome(AnyView(LabelPage.view()))
| ListBoxPage -> ValueSome(AnyView(ListBoxPage.view()))
| LayoutTransformControlPage -> ValueSome(AnyView(LayoutTransformControlPage.view()))
Expand Down
19 changes: 0 additions & 19 deletions samples/Gallery/Common.fs
Original file line number Diff line number Diff line change
Expand Up @@ -119,25 +119,6 @@ type HamburgerMenuModifiers =
static member inline expandedModeThresholdWidth(this: WidgetBuilder<'msg, IFabHamburgerMenu>, value: int) =
this.AddScalar(HamburgerMenuExt.ExpandedModeThresholdWidth.WithValue(value))

type IFabItemsControl =
inherit IFabTemplatedControl

module FabItemsControl =

let WidgetKey = Widgets.register<ItemsControl>()

[<AutoOpen>]
module FabItemsControlBuilders =
type Fabulous.Avalonia.View with

static member ItemsControl<'msg, 'itemData, 'itemMarker when 'itemMarker :> IFabControl>
(
items: seq<'itemData>,
template: 'itemData -> WidgetBuilder<'msg, 'itemMarker>
) =
WidgetHelpers.buildItems<'msg, IFabItemsControl, 'itemData, 'itemMarker> FabItemsControl.WidgetKey ItemsControl.ItemsSource items template


open Avalonia.Layout
open type Fabulous.Avalonia.View

Expand Down
1 change: 1 addition & 0 deletions samples/Gallery/Gallery.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
<Compile Include="Pages\GridSplitterPage.fs" />
<Compile Include="Pages\ImagePage.fs" />
<Compile Include="Pages\ItemsRepeaterPage.fs" />
<Compile Include="Pages\ItemsControlPage.fs" />
<Compile Include="Pages\LabelPage.fs" />
<Compile Include="Pages\LayoutTransformControlPage.fs" />
<Compile Include="Pages\ListBoxPage.fs" />
Expand Down
1 change: 1 addition & 0 deletions samples/Gallery/MainWindow.fs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ module MainWindow =
TabItem("GridSplitterPage", GridSplitterPage.view())
TabItem("ImagePage", ImagePage.view())
TabItem("ItemsRepeaterPage", ItemsRepeaterPage.view())
TabItem("ItemsControlPage", ItemsControlPage.view())
TabItem("LabelPage", LabelPage.view())
TabItem("LayoutTransformControlPage", LayoutTransformControlPage.view())
TabItem("ListBoxPage", ListBoxPage.view())
Expand Down
33 changes: 33 additions & 0 deletions samples/Gallery/Pages/ItemsControlPage.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace Gallery

open System.Diagnostics
open Avalonia
open Avalonia.Controls.Primitives
open Avalonia.Layout
open Avalonia.Media
open Fabulous.Avalonia
open Fabulous
open System.Collections.ObjectModel

open type Fabulous.Avalonia.View

module ItemsControlPage =
type Crockery = { Title: string; Number: int }

let items = [ for i in 1..1000 -> { Title = "dinner plate"; Number = i } ]

let view () =

VStack() {
TextBlock("List of crockery:")

ItemsControl(
items,
fun item ->
HStack() {
TextBlock(item.Title)
TextBlock(item.Number.ToString())
}
)
.itemsPanel(VirtualizingStackPanel())
}
3 changes: 3 additions & 0 deletions src/Fabulous.Avalonia/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
_No unreleased changes_

### Added
- `ItemsControl` and `VirtualizingStackPanel` Widgets by @edgarfgp in https://github.com/fabulous-dev/Fabulous.Avalonia/pull/247

## [3.0.0-pre5] - 2024-05-17
### Added
- TreeViewItem widget by @edgarfgp in https://github.com/fabulous-dev/Fabulous.Avalonia/pull/241
Expand Down
4 changes: 3 additions & 1 deletion src/Fabulous.Avalonia/Fabulous.Avalonia.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@
<Compile Include="Views\_TopLevel.fs" />
<Compile Include="Views\_TextElement.fs" />
<Compile Include="Views\_Panel.fs" />
<Compile Include="Views\_ItemsControl.fs" />
<Compile Include="Views\_VirtualizingPanel.fs" />
<Compile Include="Views\ItemsControl.fs" />
<Compile Include="Views\_HeaderedItemsControl.fs" />
<Compile Include="Views\_SelectingItemsControl.fs" />
<Compile Include="Views\_MenuBase.fs" />
Expand Down Expand Up @@ -211,6 +212,7 @@
<Compile Include="Views\Panels\AdornerLayer.fs" />
<Compile Include="Views\Panels\Grid.fs" />
<Compile Include="Views\Panels\StackPanel.fs" />
<Compile Include="Views\Panels\VirtualizingStackPanel.fs" />
<Compile Include="Views\Panels\ReversibleStackPanel.fs" />
<Compile Include="Views\Panels\DockPanel.fs" />
<Compile Include="Views\Panels\WrapPanel.fs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ type IFabItemsControl =
inherit IFabTemplatedControl

module ItemsControl =
let WidgetKey = Widgets.register<ItemsControl>()

let Items =
Attributes.defineAvaloniaNonGenericListWidgetCollection "ItemsControl_Items" (fun target ->
let target = target :?> ItemsControl
Expand Down Expand Up @@ -39,12 +41,12 @@ module ItemsControl =

let ItemsPanel =
Attributes.defineSimpleScalar<Widget> "ItemsControl_ItemsPanel" ScalarAttributeComparers.equalityCompare (fun _ newValueOpt node ->
let treeView = node.Target :?> TreeView
let itemsControl = node.Target :?> ItemsControl

match newValueOpt with
| ValueNone -> treeView.ClearValue(ItemsControl.ItemsPanelProperty)
| ValueNone -> itemsControl.ClearValue(ItemsControl.ItemsPanelProperty)
| ValueSome value ->
treeView.SetValue(ItemsControl.ItemsPanelProperty, WidgetItemsPanel(node, value))
itemsControl.SetValue(ItemsControl.ItemsPanelProperty, WidgetItemsPanel(node, value))
|> ignore)

let ContainerClearing =
Expand All @@ -56,6 +58,18 @@ module ItemsControl =
let ContainerPrepared =
Attributes.defineEvent "ItemsControl_ContainerPrepared" (fun target -> (target :?> ItemsControl).ContainerPrepared)


[<AutoOpen>]
module ItemsControlBuilders =
type Fabulous.Avalonia.View with

static member ItemsControl<'msg, 'itemData, 'itemMarker when 'itemMarker :> IFabControl>
(
items: seq<'itemData>,
template: 'itemData -> WidgetBuilder<'msg, 'itemMarker>
) =
WidgetHelpers.buildItems<'msg, IFabItemsControl, 'itemData, 'itemMarker> ItemsControl.WidgetKey ItemsControl.ItemsSource items template

type ItemsControlModifiers =
/// <summary>Listens to the ItemsControl ContainerClearing event.</summary>
/// <param name="this">Current widget.</param>
Expand Down
76 changes: 76 additions & 0 deletions src/Fabulous.Avalonia/Views/Panels/VirtualizingStackPanel.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
namespace Fabulous.Avalonia

open System.Runtime.CompilerServices
open Avalonia.Controls
open Avalonia.Interactivity
open Fabulous
open Fabulous.StackAllocatedCollections.StackList

type IFabVirtualizingStackPanel =
inherit IFabVirtualizingPanel

module VirtualizingStackPanel =
let WidgetKey = Widgets.register<VirtualizingStackPanel>()

let Orientation =
Attributes.defineAvaloniaPropertyWithEquality VirtualizingStackPanel.OrientationProperty

let AreHorizontalSnapPointsRegular =
Attributes.defineAvaloniaPropertyWithEquality VirtualizingStackPanel.AreHorizontalSnapPointsRegularProperty

let AreVerticalSnapPointsRegular =
Attributes.defineAvaloniaPropertyWithEquality VirtualizingStackPanel.AreVerticalSnapPointsRegularProperty

let HorizontalSnapPointsChanged =
Attributes.defineEvent "VirtualizingStackPanel_HorizontalSnapPointsChanged" (fun target ->
(target :?> VirtualizingStackPanel)
.HorizontalSnapPointsChanged)

let VerticalSnapPointsChanged =
Attributes.defineEvent "VirtualizingStackPanel_VerticalSnapPointsChanged" (fun target ->
(target :?> VirtualizingStackPanel)
.VerticalSnapPointsChanged)

[<AutoOpen>]
module VirtualizingStackPanelBuilders =
type Fabulous.Avalonia.View with

/// <summary>Creates a VirtualizingStackPanel widget.</summary>
static member VirtualizingStackPanel() =
WidgetBuilder<'msg, IFabVirtualizingStackPanel>(VirtualizingStackPanel.WidgetKey, AttributesBundle(StackList.empty(), ValueNone, ValueNone))

type VirtualizingStackPanelModifiers =
/// <summary>Sets the AreHorizontalSnapPointsRegular property.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The AreHorizontalSnapPointsRegular value.</param>
[<Extension>]
static member inline areHorizontalSnapPointsRegular(this: WidgetBuilder<'msg, #IFabVirtualizingStackPanel>, value: bool) =
this.AddScalar(VirtualizingStackPanel.AreHorizontalSnapPointsRegular.WithValue(value))

/// <summary>Sets the AreVerticalSnapPointsRegular property.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The AreVerticalSnapPointsRegular value.</param>
[<Extension>]
static member inline areVerticalSnapPointsRegular(this: WidgetBuilder<'msg, #IFabVirtualizingStackPanel>, value: bool) =
this.AddScalar(VirtualizingStackPanel.AreVerticalSnapPointsRegular.WithValue(value))

/// <summary>Listens to the StackPanel HorizontalSnapPointsChanged event.</summary>
/// <param name="this">Current widget.</param>
/// <param name="fn">Raised when the HorizontalSnapPointsChanged event fires.</param>
[<Extension>]
static member inline onHorizontalSnapPointsChanged(this: WidgetBuilder<'msg, #IFabVirtualizingStackPanel>, fn: RoutedEventArgs -> 'msg) =
this.AddScalar(VirtualizingStackPanel.HorizontalSnapPointsChanged.WithValue(fn))

/// <summary>Listens to the StackPanel VerticalSnapPointsChanged event.</summary>
/// <param name="this">Current widget.</param>
/// <param name="fn">Raised when the VerticalSnapPointsChanged event fires.</param>
[<Extension>]
static member inline onVerticalSnapPointsChanged(this: WidgetBuilder<'msg, #IFabVirtualizingStackPanel>, fn: RoutedEventArgs -> 'msg) =
this.AddScalar(VirtualizingStackPanel.VerticalSnapPointsChanged.WithValue(fn))

/// <summary>Link a ViewRef to access the direct VirtualizingStackPanel control instance.</summary>
/// <param name="this">Current widget.</param>
/// <param name="value">The ViewRef instance that will receive access to the underlying control.</param>
[<Extension>]
static member inline reference(this: WidgetBuilder<'msg, IFabVirtualizingStackPanel>, value: ViewRef<VirtualizingStackPanel>) =
this.AddScalar(ViewRefAttributes.ViewRef.WithValue(value.Unbox))
4 changes: 4 additions & 0 deletions src/Fabulous.Avalonia/Views/_VirtualizingPanel.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
namespace Fabulous.Avalonia

type IFabVirtualizingPanel =
inherit IFabPanel
17 changes: 6 additions & 11 deletions src/Fabulous.Avalonia/VirtualizedCollection.fs
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,10 @@ type WidgetTreeDataTemplate(node: IViewNode, childrenFn: obj -> IEnumerable, tem

view :?> Control

type WidgetItemsPanel(node: IViewNode, widget: Widget) =
type WidgetItemsPanel(node: IViewNode, widget: Widget) as this =
inherit FuncTemplate<Panel>(fun _ -> this.BuildPanel())

interface ITemplate<Panel> with
member this.Build() =
let definition = WidgetDefinitionStore.get widget.Key
let struct (_, view) = definition.CreateView(widget, node.TreeContext, ValueNone)
view :?> Panel

member this.Build() =
let definition = WidgetDefinitionStore.get widget.Key
let struct (_, view) = definition.CreateView(widget, node.TreeContext, ValueNone)
view
member this.BuildPanel() =
let definition = WidgetDefinitionStore.get widget.Key
let struct (_, view) = definition.CreateView(widget, node.TreeContext, ValueNone)
view :?> Panel

0 comments on commit a256fb4

Please sign in to comment.