mirror of
https://gitlab.com/sheaf/metabrush.git
synced 2024-11-06 15:23:37 +00:00
253 lines
8.9 KiB
Haskell
253 lines
8.9 KiB
Haskell
|
{-# LANGUAGE BlockArguments #-}
|
||
|
{-# LANGUAGE DataKinds #-}
|
||
|
{-# LANGUAGE DeriveGeneric #-}
|
||
|
{-# LANGUAGE DerivingStrategies #-}
|
||
|
{-# LANGUAGE FlexibleContexts #-}
|
||
|
{-# LANGUAGE FlexibleInstances #-}
|
||
|
{-# LANGUAGE FunctionalDependencies #-}
|
||
|
{-# LANGUAGE NamedFieldPuns #-}
|
||
|
{-# LANGUAGE OverloadedStrings #-}
|
||
|
{-# LANGUAGE PolyKinds #-}
|
||
|
{-# LANGUAGE RecordWildCards #-}
|
||
|
{-# LANGUAGE TypeApplications #-}
|
||
|
{-# LANGUAGE TypeFamilies #-}
|
||
|
|
||
|
module MetaBrush.UI.Menu
|
||
|
( newMenuBar
|
||
|
, Menu(..)
|
||
|
, FileMenu(..), EditMenu(..), ViewMenu(..), HelpMenu(..)
|
||
|
, ResourceType(..)
|
||
|
, MenuItem(..)
|
||
|
)
|
||
|
where
|
||
|
|
||
|
-- base
|
||
|
import Control.Monad
|
||
|
( unless )
|
||
|
import Data.Foldable
|
||
|
( for_ )
|
||
|
import Data.Kind
|
||
|
( Type )
|
||
|
import GHC.Generics
|
||
|
( Generic )
|
||
|
|
||
|
-- generic-lens
|
||
|
import Data.Generics.Product.Constraints
|
||
|
( HasConstraints(constraints) )
|
||
|
|
||
|
-- gi-gtk
|
||
|
import qualified GI.Gtk as GTK
|
||
|
|
||
|
-- text
|
||
|
import Data.Text
|
||
|
( Text )
|
||
|
|
||
|
-- transformers
|
||
|
import Control.Monad.IO.Class
|
||
|
( MonadIO(liftIO) )
|
||
|
|
||
|
-- MetaBrush
|
||
|
import MetaBrush.Render.Util
|
||
|
( widgetAddClasses )
|
||
|
|
||
|
--------------------------------------------------------------------------------
|
||
|
-- Types for describing menu items.
|
||
|
|
||
|
data ResourceType
|
||
|
= Description
|
||
|
| Object
|
||
|
|
||
|
data family MenuItem ( submenu :: ResourceType -> Type ) ( r :: ResourceType )
|
||
|
data instance MenuItem submenu Description
|
||
|
= MenuItemDescription
|
||
|
{ menuItemLabel :: !Text
|
||
|
, menuItemClasses :: ![ Text ]
|
||
|
, menuItemAction :: !( Maybe Text )
|
||
|
, submenuDescription :: !( submenu Description )
|
||
|
}
|
||
|
data instance MenuItem submenu Object
|
||
|
= MenuItem
|
||
|
{ menuItem :: !GTK.MenuItem
|
||
|
, menuItemSubmenu :: !( submenu Object )
|
||
|
}
|
||
|
|
||
|
data family Separator ( r :: ResourceType )
|
||
|
data instance Separator Description
|
||
|
= SeparatorDescription
|
||
|
{ separatorClasses :: ![ Text ] }
|
||
|
data instance Separator Object
|
||
|
= Separator
|
||
|
{ separatorItem :: !GTK.MenuItem }
|
||
|
|
||
|
data NoSubresource ( k :: ResourceType ) = NoSubresource
|
||
|
|
||
|
--------------------------------------------------------------------------------
|
||
|
-- Menu used in MetaBrush.
|
||
|
|
||
|
-- Types.
|
||
|
|
||
|
data Menu ( rt :: ResourceType )
|
||
|
= Menu
|
||
|
{ file :: !( MenuItem FileMenu rt )
|
||
|
, edit :: !( MenuItem EditMenu rt )
|
||
|
, view :: !( MenuItem ViewMenu rt )
|
||
|
, help :: !( MenuItem HelpMenu rt )
|
||
|
}
|
||
|
deriving stock Generic
|
||
|
|
||
|
data FileMenu ( rt :: ResourceType )
|
||
|
= FileMenu
|
||
|
{ new :: !( MenuItem NoSubresource rt )
|
||
|
, open :: !( MenuItem NoSubresource rt )
|
||
|
, save :: !( MenuItem NoSubresource rt )
|
||
|
, saveAs :: !( MenuItem NoSubresource rt )
|
||
|
, close :: !( MenuItem NoSubresource rt )
|
||
|
, quit :: !( MenuItem NoSubresource rt )
|
||
|
}
|
||
|
deriving stock Generic
|
||
|
|
||
|
data EditMenu ( rt :: ResourceType )
|
||
|
= EditMenu
|
||
|
{ undo :: !( MenuItem NoSubresource rt )
|
||
|
, redo :: !( MenuItem NoSubresource rt )
|
||
|
, history :: !( MenuItem NoSubresource rt )
|
||
|
, editSep1 :: !( Separator rt )
|
||
|
, cut :: !( MenuItem NoSubresource rt )
|
||
|
, copy :: !( MenuItem NoSubresource rt )
|
||
|
, paste :: !( MenuItem NoSubresource rt )
|
||
|
, duplicate :: !( MenuItem NoSubresource rt )
|
||
|
, editSep2 :: !( Separator rt )
|
||
|
, preferences :: !( MenuItem NoSubresource rt )
|
||
|
}
|
||
|
deriving stock Generic
|
||
|
|
||
|
data ViewMenu ( rt :: ResourceType )
|
||
|
= ViewMenu
|
||
|
{ navigator :: !( MenuItem NoSubresource rt )
|
||
|
, viewSep1 :: !( Separator rt )
|
||
|
, strokes :: !( MenuItem NoSubresource rt )
|
||
|
, brushes :: !( MenuItem NoSubresource rt )
|
||
|
, metaparameters :: !( MenuItem NoSubresource rt )
|
||
|
, viewSep2 :: !( Separator rt )
|
||
|
, transform :: !( MenuItem NoSubresource rt )
|
||
|
}
|
||
|
deriving stock Generic
|
||
|
|
||
|
data HelpMenu ( rt :: ResourceType )
|
||
|
= HelpMenu
|
||
|
{ about :: !( MenuItem NoSubresource rt ) }
|
||
|
deriving stock Generic
|
||
|
|
||
|
-- Descriptions.
|
||
|
|
||
|
menuDescription :: Menu Description
|
||
|
menuDescription
|
||
|
= Menu
|
||
|
{ file = MenuItemDescription "File" [ "menuItem", "file" ] Nothing fileMenuDescription
|
||
|
, edit = MenuItemDescription "Edit" [ "menuItem", "edit" ] Nothing editMenuDescription
|
||
|
, view = MenuItemDescription "View" [ "menuItem", "view" ] Nothing viewMenuDescription
|
||
|
, help = MenuItemDescription "Help" [ "menuItem", "help" ] Nothing helpMenuDescription
|
||
|
}
|
||
|
|
||
|
fileMenuDescription :: FileMenu Description
|
||
|
fileMenuDescription
|
||
|
= FileMenu
|
||
|
{ new = MenuItemDescription "New" [ "submenuItem" ] Nothing NoSubresource
|
||
|
, open = MenuItemDescription "Open" [ "submenuItem" ] Nothing NoSubresource
|
||
|
, save = MenuItemDescription "Save" [ "submenuItem" ] Nothing NoSubresource
|
||
|
, saveAs = MenuItemDescription "Save as" [ "submenuItem" ] Nothing NoSubresource
|
||
|
, close = MenuItemDescription "Close" [ "submenuItem" ] Nothing NoSubresource
|
||
|
, quit = MenuItemDescription "Quit" [ "submenuItem" ] Nothing NoSubresource
|
||
|
}
|
||
|
|
||
|
editMenuDescription :: EditMenu Description
|
||
|
editMenuDescription
|
||
|
= EditMenu
|
||
|
{ undo = MenuItemDescription "Undo" [ "submenuItem" ] Nothing NoSubresource
|
||
|
, redo = MenuItemDescription "Redo" [ "submenuItem" ] Nothing NoSubresource
|
||
|
, history = MenuItemDescription "History" [ "submenuItem" ] Nothing NoSubresource
|
||
|
, editSep1 = SeparatorDescription [ "submenuSeparator" ]
|
||
|
, cut = MenuItemDescription "Cut" [ "submenuItem" ] Nothing NoSubresource
|
||
|
, copy = MenuItemDescription "Copy" [ "submenuItem" ] Nothing NoSubresource
|
||
|
, paste = MenuItemDescription "Paste" [ "submenuItem" ] Nothing NoSubresource
|
||
|
, duplicate = MenuItemDescription "Duplicate" [ "submenuItem" ] Nothing NoSubresource
|
||
|
, editSep2 = SeparatorDescription [ "submenuSeparator" ]
|
||
|
, preferences = MenuItemDescription "Preferences" [ "submenuItem" ] Nothing NoSubresource
|
||
|
}
|
||
|
|
||
|
viewMenuDescription :: ViewMenu Description
|
||
|
viewMenuDescription
|
||
|
= ViewMenu
|
||
|
{ navigator = MenuItemDescription "Navigator" [ "submenuItem" ] Nothing NoSubresource
|
||
|
, viewSep1 = SeparatorDescription [ "submenuSeparator" ]
|
||
|
, strokes = MenuItemDescription "Strokes" [ "submenuItem" ] Nothing NoSubresource
|
||
|
, brushes = MenuItemDescription "Brushes" [ "submenuItem" ] Nothing NoSubresource
|
||
|
, metaparameters = MenuItemDescription "Metaparameters" [ "submenuItem" ] Nothing NoSubresource
|
||
|
, viewSep2 = SeparatorDescription [ "submenuSeparator" ]
|
||
|
, transform = MenuItemDescription "Transform" [ "submenuItem" ] Nothing NoSubresource
|
||
|
}
|
||
|
|
||
|
helpMenuDescription :: HelpMenu Description
|
||
|
helpMenuDescription
|
||
|
= HelpMenu
|
||
|
{ about = MenuItemDescription "About MetaBrush" [ "submenuItem" ] Nothing NoSubresource }
|
||
|
|
||
|
--------------------------------------------------------------------------------
|
||
|
-- Creating a GTK menu bar from a menu description.
|
||
|
|
||
|
newMenuItem :: MonadIO m => MenuItem submenu Description -> m GTK.MenuItem
|
||
|
newMenuItem ( MenuItemDescription { .. } ) = do
|
||
|
menuItem <- GTK.menuItemNewWithLabel menuItemLabel
|
||
|
-- Could set accelerator labels...
|
||
|
unless ( null menuItemClasses ) do
|
||
|
widgetAddClasses menuItem menuItemClasses
|
||
|
for_ menuItemAction \ actionName -> do
|
||
|
GTK.actionableSetActionName menuItem ( Just actionName )
|
||
|
pure menuItem
|
||
|
|
||
|
class CreateMenuItem desc res | desc -> res, res -> desc where
|
||
|
createMenuItem :: MonadIO m => ( GTK.MenuItem -> m () ) -> desc -> m res
|
||
|
instance {-# OVERLAPPING #-} CreateMenuItem ( MenuItem NoSubresource Description ) ( MenuItem NoSubresource Object ) where
|
||
|
createMenuItem attachToParent menuItemDescription = do
|
||
|
menuItem <- newMenuItem menuItemDescription
|
||
|
attachToParent menuItem
|
||
|
pure
|
||
|
MenuItem
|
||
|
{ menuItem = menuItem
|
||
|
, menuItemSubmenu = NoSubresource
|
||
|
}
|
||
|
instance ( HasConstraints CreateMenuItem ( submenu Description ) ( submenu Object ) )
|
||
|
=> CreateMenuItem ( MenuItem submenu Description ) ( MenuItem submenu Object )
|
||
|
where
|
||
|
createMenuItem attachToParent menuItemDescription@( MenuItemDescription { submenuDescription } ) = do
|
||
|
menuItem <- newMenuItem menuItemDescription
|
||
|
submenu <- GTK.menuNew
|
||
|
submenuItems <-
|
||
|
constraints @CreateMenuItem
|
||
|
( createMenuItem ( GTK.menuShellAppend submenu ) )
|
||
|
submenuDescription
|
||
|
GTK.menuItemSetSubmenu menuItem ( Just submenu )
|
||
|
attachToParent menuItem
|
||
|
pure
|
||
|
MenuItem
|
||
|
{ menuItem = menuItem
|
||
|
, menuItemSubmenu = submenuItems
|
||
|
}
|
||
|
instance CreateMenuItem ( Separator Description ) ( Separator Object ) where
|
||
|
createMenuItem attachToParent ( SeparatorDescription { .. } ) = do
|
||
|
separator <- GTK.separatorMenuItemNew
|
||
|
unless ( null separatorClasses ) do
|
||
|
widgetAddClasses separator separatorClasses
|
||
|
sep <- liftIO ( GTK.unsafeCastTo GTK.MenuItem separator )
|
||
|
attachToParent sep
|
||
|
pure ( Separator { separatorItem = sep } )
|
||
|
|
||
|
newMenuBar :: MonadIO m => m ( GTK.MenuBar, Menu Object )
|
||
|
newMenuBar = do
|
||
|
menuBar <- GTK.menuBarNew
|
||
|
menu <-
|
||
|
constraints @CreateMenuItem
|
||
|
( createMenuItem ( GTK.menuShellAppend menuBar ) )
|
||
|
menuDescription
|
||
|
pure ( menuBar, menu )
|