Giter Site home page Giter Site logo

dretch / gi-gtk-declarative Goto Github PK

View Code? Open in Web Editor NEW

This project forked from owickstrom/gi-gtk-declarative

1.0 1.0 0.0 1.14 MB

Declarative GTK+ programming in Haskell

Home Page: https://owickstrom.github.io/gi-gtk-declarative/

Haskell 97.59% Nix 2.08% Shell 0.33%

gi-gtk-declarative's Introduction

Project Abandoned

This project is abandoned. Use the original gi-gtk-declarative or Monomer instead.


CI

GI GTK Declarative + components + custom attributes

This is a half-finished fork of the wonderful gi-gtk-declarative that adds components and custom attributes.

Components

The original gi-gtk-declarative comes with an Elm-style architecture where there is a single event handler for all events in the application.

This fork adds something more akin to React's components. Each component has it's own internal state and its own event handler. Inside this event handler a component can update its state, do asynchronous IO, and send events (synchronously) to the parent component. Components can communicate with their children by creating new declarative widgets (rather like updating props in React).

Components are supposed to enable abstraction and code-reuse.

Complete Example

{-# LANGUAGE LambdaCase        #-}
{-# LANGUAGE OverloadedLabels  #-}
{-# LANGUAGE OverloadedLists   #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards   #-}
{-# LANGUAGE TypeFamilies      #-}

module Components where

import           Control.Concurrent             ( threadDelay )
import           Control.Monad                  ( when )
import           Control.Monad.State.Class      ( get, modify, put )
import           Data.Text                      ( pack )

import           GI.Gtk                         ( Box(..)
                                                , Button(..)
                                                , Label(..)
                                                , Orientation(..)
                                                , Window(..)
                                                , WindowPosition(..) )
import           GI.Gtk.Declarative
import           GI.Gtk.Declarative.Widget      ()
import           GI.Gtk.Declarative.Component

-- The declarative version of this component. `event` is the type of events that
-- get emitted externally - i.e. sent to the parent component.
data IncButton event = IncButton
  { incEvent :: Int -> event -- ^ constructs an event to let the parent know that
                             --   the count has been incremented.
  }

-- A declarative component must implement the `Component` class.
instance Component IncButton where

  -- The internal state of the component.
  data ComponentState IncButton = IncButtonState Int

  -- The internal events that are fired by this components internal widget tree.
  data ComponentAction IncButton = Inc | Reset

  -- Creates the initial component state, and fires an initial event.
  createComponent IncButton{} =
    (IncButtonState 0, Just Inc)

  -- Called when the declarative component is updated so that the internal state
  -- can be updated.
  patchComponent state IncButton{} =
    state

  -- Handles internal events.
  update IncButton{..} = \case
    Reset -> do
      -- Set internal state.
      put $ IncButtonState 0
      notifyParent
    Inc -> do
      -- Modify internal state.
      modify $ \(IncButtonState i) -> IncButtonState (i + 1)
      notifyParent
      -- Run some IO that sleeps and then emits another action,
      -- causing an infinite loop of events (until the component
      -- is removed from the widget tree).
      updateIO $ Just Inc <$ threadDelay 1000000
    where
      notifyParent = do
        -- Get the internal state.
        IncButtonState i <- get
        -- Do some IO (without firing an event when it finishes):
        updateIO_ $ putStrLn ("about to tell parent that i = " <> show i)
        -- Send a message to the parent component:
        updateParent $ incEvent i

  -- Creates the declarative widget tree for this component.
  view IncButton{} (IncButtonState i) =
    widget Button
      [ #label := pack ("Reset (i = " <> show i <> ") to 0")
      , on #clicked Reset
      ]

-- The root component.
data App event = App
  { exitEvent :: event -- ^ This event lets the runtime know to quit the app.
  }

instance Component App where

  data ComponentState App = AppState

  data ComponentAction App = ReceiveInc Int | CloseWindow

  createComponent App{} =
    (AppState, Nothing)

  update App{..} = \case
    CloseWindow ->
      updateParent exitEvent
    ReceiveInc i -> do
      updateIO_ $ putStrLn ("a child told us that i = " <> show i)
      when (i == 10) $ do
        updateIO_ $ putStrLn "i == 10, that's enough!"
        updateParent exitEvent

  view App{} AppState =
    bin
      Window
      [ #title := "Components"
      , on #deleteEvent (const (True, CloseWindow))
      , #heightRequest := 100
      , #windowPosition := WindowPositionCenter
      ] $
      container Box
        [ #orientation := OrientationVertical
        , #margin := 4
        ]
        [ BoxChild
            defaultBoxChildProperties
            (widget Label [ #label := "The app will finish when i = 10" ])
        , BoxChild
            defaultBoxChildProperties
            -- Components can be used anywhere a regular widget can be used
            -- they just need to be turned into a `Widget e` via the `component`
            -- method. We pass `ReceiveInc` so that `IncButton` knows how to
            -- construct an event that `App` understands.
            (component (IncButton ReceiveInc))
        ]

-- This will start the GTK event loop, render the component widget tree, and keep
-- running component `update` methods when events occur, until the `Exit` event
-- is emitted by `App`.
main :: IO ()
main = run App{ exitEvent = Exit () }

Bigger Example

This is an application built with components: https://github.com/Dretch/foundationdb-explorer/

Custom Attributes

Normal attributes allow declaring values for GTK properties. However, often the GTK API requires you to call a method on a widget rather than write to an attribute.

Custom attributes maintain arbitrary internal state and patching behaviour, so they can call any GTK methods you like. This is rather like CustomWidget in gi-gtk-declarative, but in a more composable manner: you can use any number of custom attributes on any widget.

Examples

Status

Unfinished, badly designed, undocumented, full of bugs. Really just for information at this point.

FAQ

Q. Why build this as a fork instead of a module (like gi-gtk-declarative-app-simple)?

A 1. Custom attributes are, I think, impossible to implement as a module, because it needs changes throughout the core of the codebase.

A 2. It seems almost possible to provide the component system as a module, but there is one blocker that so far I cannot solve. The Functor constraint on widgets means that events can't be Typeable, which is required in order to implement the dynamic typing involved in sending events to their parent components.

Q. How does a running component decide to exit the application?

A. The root component sends an Exit x event to its' parent (via updateParent), and this causes the runtime to stop running the GTK event loop and return the x value to the program that started the component in the first place.

Q. Where can I find API Docs?

A. API Docs

Q. What happens if the view created by the top-level event is not a Window widget?

A. Wierd stuff happens, unfortunately. In future the type system might enforce that root components always creates a window.

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.