Skip to content

Commit

Permalink
Merge pull request #52 from fabulous-dev/add-missing-modifiers
Browse files Browse the repository at this point in the history
Add missing modifiers
  • Loading branch information
TimLariviere authored Dec 12, 2023
2 parents 3979c18 + 1d205ff commit 768398a
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 2 deletions.
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

_No unreleased changes_

## [8.0.2] - 2023-12-12

### Added
- Added additional Any widgets to support Memo widget as a root by @TimLariviere (https://github.com/fabulous-dev/Fabulous.MauiControls/pull/52)
- Added missing Cell.ContextActions and MultiPage.CurrentPage modifiers by @TimLariviere (https://github.com/fabulous-dev/Fabulous.MauiControls/pull/52)

## [8.0.1] - 2023-11-14

### Fixed
Expand Down Expand Up @@ -134,7 +140,8 @@ Essentially v2.8.1 and v8.0.0 are similar except for the required .NET version.
### Changed
- Fabulous.MauiControls has moved from the Fabulous repository to its own repository: [https://github.com/fabulous-dev/Fabulous.MauiControls](https://github.com/fabulous-dev/Fabulous.MauiControls)

[unreleased]: https://github.com/fabulous-dev/Fabulous.MauiControls/compare/8.0.1...HEAD
[unreleased]: https://github.com/fabulous-dev/Fabulous.MauiControls/compare/8.0.2...HEAD
[8.0.2]: https://github.com/fabulous-dev/Fabulous.MauiControls/releases/tag/8.0.2
[8.0.1]: https://github.com/fabulous-dev/Fabulous.MauiControls/releases/tag/8.0.1
[8.0.0]: https://github.com/fabulous-dev/Fabulous.MauiControls/releases/tag/8.0.0
[2.8.1]: https://github.com/fabulous-dev/Fabulous.MauiControls/releases/tag/2.8.1
Expand Down
8 changes: 8 additions & 0 deletions src/Fabulous.MauiControls/AppHostBuilderExtensions.fs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ type AppHostBuilderExtensions =
static member UseFabulousApp(this: MauiAppBuilder, program: Program<unit, 'model, 'msg, #IFabApplication>) : MauiAppBuilder =
this.UseMauiApp(fun (_serviceProvider: IServiceProvider) -> (Program.startApplication program) :> Microsoft.Maui.IApplication)

[<Extension>]
static member UseFabulousApp(this: MauiAppBuilder, program: Program<unit, 'model, 'msg, Memo.Memoized<#IFabApplication>>) : MauiAppBuilder =
this.UseMauiApp(fun (_serviceProvider: IServiceProvider) -> (Program.startApplicationMemo program) :> Microsoft.Maui.IApplication)

[<Extension>]
static member UseFabulousApp(this: MauiAppBuilder, program: Program<'arg, 'model, 'msg, #IFabApplication>, arg: 'arg) : MauiAppBuilder =
this.UseMauiApp(fun (_serviceProvider: IServiceProvider) -> (Program.startApplicationWithArgs arg program) :> Microsoft.Maui.IApplication)

[<Extension>]
static member UseFabulousApp(this: MauiAppBuilder, program: Program<'arg, 'model, 'msg, Memo.Memoized<#IFabApplication>>, arg: 'arg) : MauiAppBuilder =
this.UseMauiApp(fun (_serviceProvider: IServiceProvider) -> (Program.startApplicationWithArgsMemo arg program) :> Microsoft.Maui.IApplication)
19 changes: 19 additions & 0 deletions src/Fabulous.MauiControls/Program.fs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,14 @@ module Program =
=
define init update view

/// Create a program using an MVU loop. Add support for Cmd
let statefulWithCmdMemo
(init: 'arg -> 'model * Cmd<'msg>)
(update: 'msg -> 'model -> 'model * Cmd<'msg>)
(view: 'model -> WidgetBuilder<'msg, Memo.Memoized<#IFabApplication>>)
=
define init update view

/// Create a program using an MVU loop. Add support for CmdMsg
let statefulWithCmdMsg
(init: 'arg -> 'model * 'cmdMsg list)
Expand All @@ -141,6 +149,17 @@ module Program =
/// Start the program
let startApplication (program: Program<unit, 'model, 'msg, #IFabApplication>) : Application = startApplicationWithArgs () program

/// Start the program
let startApplicationWithArgsMemo (arg: 'arg) (program: Program<'arg, 'model, 'msg, Memo.Memoized<#IFabApplication>>) : Application =
let runner = Runners.create program
runner.Start(arg)
let adapter = ViewAdapters.create ViewNode.get runner
let view = adapter.CreateView()
unbox view

/// Start the program
let startApplicationMemo (program: Program<unit, 'model, 'msg, Memo.Memoized<'marker>>) : Application = startApplicationWithArgsMemo () program

/// Subscribe to external source of events.
/// The subscription is called once - with the initial model, but can dispatch new messages at any time.
let withSubscription (subscribe: 'model -> Cmd<'msg>) (program: Program<'arg, 'model, 'msg, 'marker>) =
Expand Down
15 changes: 15 additions & 0 deletions src/Fabulous.MauiControls/Views/Any.fs
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,27 @@ module AnyBuilders =
static member AnyView(this: WidgetBuilder<'msg, #IFabView>) =
WidgetBuilder<'msg, IFabView>(this.Key, this.Attributes)

/// <summary>Downcast to View widget to allow to return different types of views in a single expression (e.g. if/else, match with pattern, etc.)</summary>
/// <param name="this">Current widget</param>
static member AnyView(this: WidgetBuilder<'msg, Memo.Memoized<#IFabView>>) =
WidgetBuilder<'msg, Memo.Memoized<IFabView>>(this.Key, this.Attributes)

/// <summary>Downcast to Page widget to allow to return different types of pages in a single expression (e.g. if/else, match with pattern, etc.)</summary>
/// <param name="this">Current widget</param>
static member AnyPage(this: WidgetBuilder<'msg, #IFabPage>) =
WidgetBuilder<'msg, IFabPage>(this.Key, this.Attributes)

/// <summary>Downcast to Page widget to allow to return different types of pages in a single expression (e.g. if/else, match with pattern, etc.)</summary>
/// <param name="this">Current widget</param>
static member AnyPage(this: WidgetBuilder<'msg, Memo.Memoized<#IFabPage>>) =
WidgetBuilder<'msg, Memo.Memoized<IFabPage>>(this.Key, this.Attributes)

/// <summary>Downcast to Cell widget to allow to return different types of cells in a single expression (e.g. if/else, match with pattern, etc.)</summary>
/// <param name="this">Current widget</param>
static member AnyCell(this: WidgetBuilder<'msg, #IFabCell>) =
WidgetBuilder<'msg, IFabCell>(this.Key, this.Attributes)

/// <summary>Downcast to Cell widget to allow to return different types of cells in a single expression (e.g. if/else, match with pattern, etc.)</summary>
/// <param name="this">Current widget</param>
static member AnyCell(this: WidgetBuilder<'msg, Memo.Memoized<#IFabCell>>) =
WidgetBuilder<'msg, Memo.Memoized<IFabCell>>(this.Key, this.Attributes)
20 changes: 20 additions & 0 deletions src/Fabulous.MauiControls/Views/Cells/_Cell.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ namespace Fabulous.Maui

open System.Runtime.CompilerServices
open Fabulous
open Fabulous.StackAllocatedCollections
open Microsoft.Maui.Controls

type IFabCell =
Expand Down Expand Up @@ -30,6 +31,9 @@ module Cell =
let Tapped =
Attributes.defineEventNoArg "Cell_Tapped" (fun target -> (target :?> Cell).Tapped)

let ContextActions =
Attributes.defineListWidgetCollection "Cell_ContextActions" (fun target -> (target :?> Cell).ContextActions)

[<Extension>]
type CellModifiers =
/// <summary>Set a value that indicates whether the cell is enabled</summary>
Expand Down Expand Up @@ -66,3 +70,19 @@ type CellModifiers =
[<Extension>]
static member inline onTapped(this: WidgetBuilder<'msg, #IFabCell>, msg: 'msg) =
this.AddScalar(Cell.Tapped.WithValue(MsgValue(msg)))

/// <summary>Set the context actions of the cell</summary>
/// <param name="this">Current widget</param>
[<Extension>]
static member inline contextActions<'msg, 'marker when 'marker :> IFabCell>(this: WidgetBuilder<'msg, 'marker>) =
WidgetHelpers.buildAttributeCollection<'msg, 'marker, IFabMenuItem> Cell.ContextActions this

[<Extension>]
type CellYieldExtensions =
[<Extension>]
static member inline Yield<'msg, 'marker, 'itemType when 'marker :> IFabCell and 'itemType :> IFabMenuItem>
(
_: AttributeCollectionBuilder<'msg, 'marker, IFabMenuItem>,
x: WidgetBuilder<'msg, 'itemType>
) : Content<'msg> =
{ Widgets = MutStackArray1.One(x.Compile()) }
59 changes: 58 additions & 1 deletion src/Fabulous.MauiControls/Views/Pages/_MultiPageOfPage.fs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
namespace Fabulous.Maui

open System
open Fabulous
open Fabulous.ScalarAttributeDefinitions
open Microsoft.Maui.Controls
open System.Runtime.CompilerServices

Expand All @@ -11,14 +13,69 @@ module MultiPageOfPage =
let Children =
Attributes.defineListWidgetCollection "MultiPageOfPage" (fun target -> (target :?> MultiPage<Page>).Children)

[<Obsolete("Use CurrentPageWithEvent instead")>]
let CurrentPageChanged =
Attributes.defineEventNoArg "MultiPageOfPage_CurrentPageChanged" (fun target -> (target :?> MultiPage<Page>).CurrentPageChanged)

let CurrentPageWithEvent =
let name = "MultiPageOfPage_CurrentPageWithEvent"

let key =
SimpleScalarAttributeDefinition.CreateAttributeData(
ScalarAttributeComparers.noCompare,
(fun oldValueOpt (newValueOpt: ValueEventData<int, int> voption) node ->
let target = node.Target :?> MultiPage<Page>
let event = target.CurrentPageChanged

match newValueOpt with
| ValueNone ->
// The attribute is no longer applied, so we clean up the event
match node.TryGetHandler(name) with
| ValueNone -> ()
| ValueSome handler -> event.RemoveHandler(handler)

// Only clear the property if a value was set before
match oldValueOpt with
| ValueNone -> ()
| ValueSome _ -> target.CurrentPage <- target.Children.[0]

| ValueSome curr ->
// Clean up the old event handler if any
match node.TryGetHandler(name) with
| ValueNone -> ()
| ValueSome handler -> event.RemoveHandler(handler)

// Set the new value
target.CurrentPage <- target.Children.[curr.Value]

// Set the new event handler
let handler =
EventHandler(fun sender args ->
let multiPage = sender :?> MultiPage<Page>
let currentPageIndex = multiPage.Children.IndexOf(multiPage.CurrentPage)
let (MsgValue r) = curr.Event currentPageIndex
Dispatcher.dispatch node r)

node.SetHandler(name, ValueSome handler)
event.AddHandler(handler))
)
|> AttributeDefinitionStore.registerScalar

{ Key = key; Name = name }: SimpleScalarAttributeDefinition<ValueEventData<int, int>>

[<Extension>]
type MultiPageOfPageModifiers =
/// <summary>Listen for the CurrentPageChanged event</summary>
/// <param name="this">Current widget</param>
/// <param name="msg">Message to dispatch</param>
[<Extension>]
[<Extension; Obsolete("Use currentPage instead")>]
static member inline onCurrentPageChanged(this: WidgetBuilder<'msg, #IFabMultiPageOfPage>, msg: 'msg) =
this.AddScalar(MultiPageOfPage.CurrentPageChanged.WithValue(MsgValue(msg)))

Check warning on line 73 in src/Fabulous.MauiControls/Views/Pages/_MultiPageOfPage.fs

View workflow job for this annotation

GitHub Actions / release

This construct is deprecated. Use CurrentPageWithEvent instead

Check warning on line 73 in src/Fabulous.MauiControls/Views/Pages/_MultiPageOfPage.fs

View workflow job for this annotation

GitHub Actions / release

This construct is deprecated. Use CurrentPageWithEvent instead

/// <summary>Set the current page and listen for changes</summary>
/// <param name="this">Current widget</param>
/// <param name="currentPage">The current page index</param>
/// <param name="onCurrentPageChanged">Function to invoke</param>
[<Extension>]
static member inline currentPage(this: WidgetBuilder<'msg, #IFabMultiPageOfPage>, currentPage: int, onCurrentPageChanged: int -> 'msg) =
this.AddScalar(MultiPageOfPage.CurrentPageWithEvent.WithValue(ValueEventData.create currentPage onCurrentPageChanged))

0 comments on commit 768398a

Please sign in to comment.