Git Product home page Git Product logo

comet's Introduction

Comet ☄️

dev-build Clancey.Comet on fuget.org Chat on Discord

What is Comet? Comet is a modern way of writing cross-platform UIs. Based on .NET MAUI, it follows the Model View Update (MVU) pattern and magically databinds for you!

Watch this video to get a preview of the developer experience:

Video Demo

Getting Started

When you're ready to take a ride on the Comet, head over to the wiki and follow the Getting Started guide.

Key Concepts

Comet is based on the MVU architecture:

MVU pattern

View is a screen. Views have a Body method that you can assign either by using an attribute [Body]:

public class MyPage : View {
    [Body]
    View body () => new Text("Hello World");
}

Or manually from your constructor:

public class MyPage : View {
    public MyPage() {
        Body = body;
    }
    View body () => new Text("Hello World");
}

Hot Reload

Using Hot Reload is the fastest way to develop your user interface.

The setup is simple and only requires a few steps:

  1. Install the Visual Studio extension Comet.Reload from Releases (or Comet for .NET Mobile if you use Visual Studio Code)
  2. Install the Comet project template available on NuGet.
  3. Add this short snippet to your AppDelegate.cs and/or MainActivity.cs, or equivalent.
#if DEBUG
Comet.Reload.Init();
#endif

See the sample projects here for examples.

State

As of right now there are two supported ways to add state.

1. Simple data types like int, bool?

Just add a State<T> field to your View

class MyPage : View {
    readonly State<int> clickCount = 1;
}

View is state aware. When the state changes, databinding will automatically update, or rebuild the view as needed.

2. Do you want to use more complex data types?

You can either implement INotifyPropertyRead or you can use BindingObject to make it even simpler.

Add it as a Field/Property, and add the [State] attribute!

public class MainPage : View {
    class MyBindingObject : BindingObject {
        public bool CanEdit {
            get => GetProperty<bool> ();
            set => SetProperty (value);
        }
        public string Text {
            get => GetProperty<string> ();
            set => SetProperty (value);
        }
    }

    [State]
    readonly MyBindingObject state;
}

INotifyPropertyRead is just like INotifyPropertyChanged. Just call PropertyRead whenever a property getter is called. And PropertyChanged whenever a property value changes.

How do I use the State?

Simply update the stateful value and the framework handles the rest.

public class MyPage : View {

    readonly State<int> clickCount = 1;
    readonly State<string> text = "Hello World";

    public MyPage() {
        Body = () => new VStack {
            new Text (text),
            new Button("Update Text", () => state.Text = $"Click Count: {clickCount.Value++}")
        };

    }
}

That is all!, now when the text changes everything updates.

What if I want to format my value without an extra state property?

While new Button("Update Text", () => state.Text = $"Click Count: {clickCount.Value++}" ) works, it isn't efficient.

Instead, use new Text(()=> $"Click Count: {clickCount}").

public class MyPage : View {

    readonly State<int> clickCount = new State<int> (1);

    public MyPage() {
        Body = () => new VStack {
            new Text (() => $"Click Count: {clickCount}"),
            new Button("Update Text", () => {
                clickCount.Value++;
            }
        };
    }
}

What platforms are supported?

Comet is developed on top of .NET MAUI handlers, providing its own implementation for interfaces such as Microsoft.Maui.IButton and other controls. Any platform supported by .NET MAUI can be targeted:

  • Windows
  • Android
  • iOS
  • macOS
  • Blazor

Non-MAUI application models, such as UWP or WPF, are not supported.

Disclaimer

Comet is a proof of concept. There is no official support. Use at your own risk.

comet's People

Contributors

alexblount avatar alexeystrakh avatar benbtg avatar clancey avatar davidkarlas avatar davidortinau avatar davidwengier avatar denisivanitskyi avatar devronb avatar dgellow avatar filipoff2 avatar flyperstudio avatar jacob-maristany avatar jeremyvignelles avatar jessejiang0214 avatar jonlipsky avatar libin85 avatar matissehack avatar mattleibow avatar maxbrister avatar nabond251 avatar nerocui avatar nmilcoff avatar pieeatingninjas avatar rdavisau avatar rogihee avatar ronnygunawan avatar sweekriti91 avatar terrajobst avatar twsouthwick avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

comet's Issues

Context

We need a way to define bindings, properties, and other things within a ViewBuilder Context

Binding issue,

Global State inside Initializer syntax is not causing a rebuild.

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Comet;

namespace Tasky
{
    public class MainPage : View
    {

        readonly ObservableCollection<TodoItem> items = new ObservableCollection<TodoItem>{
            new TodoItem{
                Name = "Hi",
                Done = true,
            },
            new TodoItem
            {
                Name ="Finish Tasky",
            }
        };

        readonly State<bool> isAdding = false;

        [Body]
        View body() => new NavigationView{
            isAdding.Value ? (View)new VStack
            {

            } :

            new ListView<TodoItem>(items){
                ViewFor = (item)=>new VStack{
                    new HStack
                    {
                        new Text(item.Name).Frame(alignment: Alignment.Leading),
                        new Spacer(),
                        new Toggle(item.Done).Frame(alignment:Alignment.Center)
                    }.Padding(6)
                }.FillHorizontal()
            }.Title("Tasky")
        }.SetRightActionButton(new Tuple<string, Action>("+", AddItem));

        void AddItem()
        {
            isAdding.Value = true;
            Console.WriteLine("Add clicked!!!!");
        }
    }
}

Replace static services with dependency injection

Currently, there is a Device class that contains a number of static services. This service locator pattern is difficult to use (and is in opposition with) the dependency injection system used in ASP.NET Core (such as for Blazor). Can this be replaced with dependency injection so that services can be registered and retrieved via injection rather than static fields? I propose using Microsoft.Extensions.DependencyInjection and IServiceProvider as the default implementation since most containers have a way of wrapping it to provide their own implementations. I am unsure how this works on Xamarin platforms, but I've been happy with the use on UWP, .NET Core, and .NET Framework.

Cleanup/Dispose

When we are done with a View, we need to clean it up and its children.

Add Styles

Right now you can set a global FontSize, but you can't do Label.FontSize, and Button.FontSize. We also may need style id, and let people set things like StyleId.Label.FontSize or StyleId.FontSize

Setting environment inside body, can cause infinite loop

 public class MediaItemView : View
    {
        [Environment]
        readonly MediaItemBase item;
        public MediaItemView()
        {
        }
        [Body]
        View body() => new HStack
                {
                    new ArtworkView()
                    .SetEnvironment(nameof(item),item)
                    .Frame(44,44,alignment:Alignment.Leading)
                    .Padding(5),
                    new VStack{
                        new Text(item?.Name),
                        new Text(item?.DetailText)
                    }.FillHorizontal()
                }.Frame(alignment: Alignment.Leading).Padding(5);
    }

Content view doesn't layout right

Screenshot 2019-09-06 12 40 32

 public class MainPage : View
    {

        readonly ObservableCollection<TodoItem> items = new ObservableCollection<TodoItem>{
            new TodoItem{
                Name = "Hi",
                Done = true,
            },
            new TodoItem
            {
                Name ="Finish Tasky",
            }
        };


        [Body]
        View body() => new NavigationView{ 
            new ListView<TodoItem>(items){
                ViewFor = (item)=>new ContentView{
                    new HStack
                    {
                        new Text(item.Name).Frame(alignment: Alignment.Leading),
                        new Spacer(),
                        new Toggle(item.Done).Frame(alignment:Alignment.Center)
                    }.Padding(6)
                }.FillHorizontal()
            }.Title("Tasky"),
        };
    }

IText

We need an interface for Text, it should handle Text,Font,Color... All those Text properties. It will be used by Text, TextField,Button. Then we can re-use the mapper.

[Feature] AutoWire HotUi.Reload.Init

I noticed in the readme the following

 #if DEBUG
            HotUI.Reload.Init();
 #endif

Some folks sorta dispise compiler directives, I'm wondering if it'd be worth it to simply call that internally and decorate the method with a conditional attribute?:

namespace HotUi
{
    public class Reload
    {
        [Conditional("DEBUG")]
        internal void InitInternal()
        {
            // setup hot reload by default 
        }

        [Conditional("DEBUG")]
        public void Disable()
        {
            // method to allow developers to opt out of hot reload
        }
    }
}

By doing this, we get hot reload for free all the time without even having to think about it. Then we can "OptOut" by calling something like this

HotUI.Reload.Disable(); // opt out of hot reload

Support for GTK

I see that Comet is planning support for all major platforms except desktop linux.

Would love to see Comet supporting linux via GTK#

Layout in Navigation is broken

View body() => new NavigationView{ new VStack
            {
                new Text(() => $"Value: {count.Value}")
                    .Color(Color.Black)
                    .FontSize(32),
                new Button("Increment", () => count.Value ++ )
                    .Frame(width:320, height:44)
                    .Background(Color.Black)
                    .Color(Color.White)
                    .Padding(20)
                ,
            }
        };

Screenshot 2019-09-06 12 15 40

NuGet packages should make a distinction between app platform and framework

Currently, there is a NuGet package that contains everything in one package. This is great for simplicity in adding the package; however, it breaks when a target framework moniker (TFM) supports multiple app models.

For example, right now the WPF support is set for .NET Framework 4.5. However, WPF is supported in .NET Core 3.0. Blazor also runs on .NET Core 3.0. Having both of these in the same package is impossible, unless you want the blazor dependencies in a WPF app or vice versa. This is not ideal.

I propose splitting up the package so that they make a distinction of the app model being targeted. ie having the following packages:

  • Comet: Contains the .NET Standard library that every other package depends on
  • Comet.WPF: Contains a .NET Framework and .NET Core build of the WPF support
  • Comet.iOS: Contains the Xamarin iOS support
  • Comet.Android: Contains the Xamarin android support
  • Comet.UWP: Contains the UWP support
  • Comet.Mac: Contains the Mac support
  • Comet.Blazor: Contains support for the Blazor framework

This could also be packaged as the following (I'm not super familiar with Xamarin conventions so this may not be a good idea):

  • Comet: Contains the .NET Standard library that every other package depends on
  • Comet.Windows: Contains the WPF support for .NET Framework and .NET Core 3.0 and UWP for uap
  • Comet.Xamarin: Contains the xamarin support for Android/iOS/Mac
  • Comet.Blazor: Contains support for the Blazor framework for .NET Core 3.0

These are some suggestions; the big issue is separating out the app platforms from the frameworks they support since NuGet packages can only be targeted by TFM and some TFMs support multiple platforms.

Padding issue

Screenshot 2019-09-06 12 44 33

```cs using System; using System.Collections.Generic; using System.Collections.ObjectModel; using Comet;

namespace Tasky
{
public class MainPage : View
{

    readonly ObservableCollection<TodoItem> items = new ObservableCollection<TodoItem>{
        new TodoItem{
            Name = "Hi",
            Done = true,
        },
        new TodoItem
        {
            Name ="Finish Tasky",
        }
    };


    [Body]
    View body() => new NavigationView{ 
        new ListView<TodoItem>(items){
            ViewFor = (item)=>new VStack{
                new HStack
                {
                    new Text(item.Name).Frame(alignment: Alignment.Leading),
                    new Spacer(),
                    new Toggle(item.Done).Frame(alignment:Alignment.Center)
                }.Padding(6)
            }.Padding(left:12).FillHorizontal()
        }.Title("Tasky"),
    };
}

}

Layout Bug

The following code sample should have the switch on the right and the label on the left:
They are both left aligned

public class MainPage : View
    {
        readonly State<int> count = 0;

        readonly ObservableCollection<TodoItem> items = new ObservableCollection<TodoItem>{
            new TodoItem{
                Name = "Hi",
            }
        };


        [Body]
        View body() => new NavigationView{ 
            new ListView<TodoItem>(items){
                ViewFor = (item)=>new HStack
                {
                    new Text(item.Name).Frame(alignment: Alignment.Leading),
                    new Toggle(item.Done).Frame(alignment:Alignment.Center)
                }.FillHorizontal().Background(Color.Blue)
            }.Title("Tasky"),
        };
    }

Stack

Stack needs defined. We need a way to specify items way of laying out in a stack

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.