Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/simre1/reactive-markup-old2
https://github.com/simre1/reactive-markup-old2
declarative-ui gtk haskell
Last synced: 9 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/simre1/reactive-markup-old2
- Owner: Simre1
- License: mit
- Created: 2022-02-28T22:36:26.000Z (almost 3 years ago)
- Default Branch: main
- Last Pushed: 2022-11-17T20:15:36.000Z (about 2 years ago)
- Last Synced: 2024-11-16T06:21:33.329Z (2 months ago)
- Topics: declarative-ui, gtk, haskell
- Language: Haskell
- Homepage:
- Size: 1.31 MB
- Stars: 1
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: Readme.md
- License: LICENSE
Awesome Lists containing this project
README
# Reactive Markup
Reactive markup is a haskell library for declaratively defining user interfaces. It is currently still in development phase, so drastic changes happen regularly.
Some of the features of this library are:
- Declarative components
- UI as a function from model to components
- Automatic updating of the UI on model changes
- Contexts to constrain where components can be used
- Customizable interpretation of components (currently, only a GTK backend is available)This library tries to disallow all UI errors at compile time, so compiled code will definitely produce a working UI.
Here is a small code example and an image of the corresponding GTK UI:
```
import ReactiveMarkup.Target.Gtk
import ReactiveMarkup
import Data.Voidmain :: IO ()
main = do
runGtk apprenderGUI :: Markup Gtk Root Void
renderGUI = bold "Hello Reactive Markup"app :: App Gtk EmptyF Void
app =
App
{ appRender = \_ -> renderGUI,
appHandleEvent = absurd,
appInitialState = EmptyF,
appName = "Hello Reactive Markup"
}
```![Gtk Hello Reactive-Markup example](hello.png)
## How to build yourself
First and foremost, you need to have GTK 4 installed! Then the following should do the trick:
```
git clone https://github.com/Simre1/reactive-markup.git
cd reactive-markup
cabal build all
```This will build the `reactive-markup` library, the `reactive-markup-gtk` backend as well as the GTK examples. *Initial* compilation will take quite a while due to the GTK dependencies.
## Use as a library
To use Reactive Markup as a library, you will have to clone the git repository and add the reactive-markup folder manually to your build environment. Otherwise, it can be used like any other haskell library.
## Quick Tutorial
### UI components
You can define your UI by builing up the `Markup` type using the available components like `text`, `button`, `column` and so on. For example, to create a list consisting of some text and a button:
```haskell
textAndButton :: Markup Gtk Common Void
textAndButton = column
[ italic "Some text",
button "Click me"
]
```You can use functions and let-expressions to factor out code and make reusable UI components.
```haskell
textAndButton :: Markup Gtk Common Void
textAndButton =
let boldText = bold "Bold text"
in column
[ boldText,
italic boldText,
button "Click me"
]
```### Reacting to changes
Assuming that you create your UI as a function from model state to components, then the UI will automatically update itself on model state changes. However, some boilerplate is needed.
Here is an example:
```haskell
searchComponent :: Dynamic Gtk Bool -> Markup Gtk Common Void
searchComponent isBool = dynamicMarkup isBool $ \actualIsBool -> row
[if actualIsBool then "Active" else "Inactive"]
```The `Dynamic` part means that the `Bool` value may change. To actually get at the `Bool` value, you need to use `dynamicMarkup` which gives you access to the `Bool` value in the function argument `actualIsBool`. Whenever the `Bool` value changes, `dynamicMarkup` will use the given function to determine the new UI.
### Handling Events
Components can spawn events which are then passed upwards implicitly through the component hierarchy.
Here is a button which emits an event of type `Text` and the value "Event message":
```haskell
buttonWithTextEvent :: Markup Gtk Common Text
buttonWithTextEvent = button "Click" (#click ?= "Event message")
```Take note that the event type is part of the `Markup` type. This means that by looking at the type we can determine the type of the events that a component can spawn. `buttonWithTextEvent` spawns events of type `Text`.
If we use `buttonWithTextEvent` within another component, then the resuling component also spawns events of type `Text`.
```haskell
columnWithTextEvent :: Markup Gtk Common Text
columnWithTextEvent = column [ buttonWithTextEvent ]
```### First dynamic behavior
You cannot directly create `Dynamic` values and you cannot directly interact with events either. However, there are components which you can use to do so.
For example `simpleLocalState`:
```haskell
countingButton :: Markup Gtk Common Void
countingButton = simpleLocalState handleButtonClick initialState buttonWithNumber
where
initialState :: Int
initialState = 0handleButtonClick :: () -> Int -> SimpleUpdate Int Void
handleButtonClick () state = setSimpleUpdate (state + 1) defSimpleUpdate
buttonWithNumber :: Dynamic Gtk Int -> Markup Gtk Common ()
buttonWithNumber int = dynamicMarkup int $ \i -> button (string $ show i) (#click ?~ ())
````simpleLocalState` allows you to have some local state for a component and update it based on events happening within that component. In this case, the local state is an int which stores how many times the button has been clicked. Whenever the button has been clicked `handleButtonClick` is used to increase the state by 1. `buttonWithNumber` defines how to create UI components based on the local state.