monomer 1.0.0.3 → 1.1.0.0
raw patch · 58 files changed
+965/−121 lines, 58 filesdep +OpenGLRawnew-component:exe:opengl
Dependencies added: OpenGLRaw
Files
- ChangeLog.md +13/−0
- README.md +1/−0
- cbits/fontmanager.c +1/−0
- examples/books/BookTypes.hs +10/−0
- examples/books/Main.hs +20/−7
- examples/generative/GenerativeTypes.hs +11/−0
- examples/generative/Main.hs +10/−0
- examples/generative/Widgets/BoxesPalette.hs +10/−0
- examples/generative/Widgets/CirclesGrid.hs +10/−0
- examples/opengl/Main.hs +101/−0
- examples/opengl/OpenGLWidget.hs +239/−0
- examples/ticker/BinanceTypes.hs +10/−0
- examples/ticker/Main.hs +10/−0
- examples/ticker/TickerTypes.hs +10/−0
- examples/todo/Main.hs +10/−0
- examples/todo/TodoTypes.hs +10/−0
- examples/tutorial/Main.hs +10/−0
- examples/tutorial/Tutorial01_Basics.hs +10/−0
- examples/tutorial/Tutorial02_Styling.hs +10/−0
- examples/tutorial/Tutorial03_LifeCycle.hs +10/−0
- examples/tutorial/Tutorial04_Tasks.hs +10/−0
- examples/tutorial/Tutorial05_Producers.hs +10/−0
- examples/tutorial/Tutorial06_Composite.hs +10/−0
- examples/tutorial/Tutorial07_CustomWidget.hs +10/−0
- examples/tutorial/Tutorial08_Themes.hs +10/−0
- monomer.cabal +45/−1
- src/Monomer/Common/BasicTypes.hs +4/−0
- src/Monomer/Core/Combinators.hs +0/−1
- src/Monomer/Core/SizeReq.hs +50/−2
- src/Monomer/Core/ThemeTypes.hs +12/−0
- src/Monomer/Core/WidgetTypes.hs +8/−1
- src/Monomer/Graphics/Types.hs +1/−1
- src/Monomer/Helper.hs +9/−0
- src/Monomer/Main/Core.hs +8/−0
- src/Monomer/Main/Handlers.hs +29/−4
- src/Monomer/Main/Types.hs +1/−1
- src/Monomer/Widgets/Composite.hs +2/−1
- src/Monomer/Widgets/Container.hs +17/−5
- src/Monomer/Widgets/Containers/Box.hs +7/−6
- src/Monomer/Widgets/Containers/Dropdown.hs +1/−1
- src/Monomer/Widgets/Containers/Grid.hs +7/−6
- src/Monomer/Widgets/Containers/Scroll.hs +40/−15
- src/Monomer/Widgets/Containers/Stack.hs +7/−6
- src/Monomer/Widgets/Single.hs +8/−3
- src/Monomer/Widgets/Singles/Button.hs +4/−20
- src/Monomer/Widgets/Singles/ExternalLink.hs +5/−15
- src/Monomer/Widgets/Singles/Image.hs +21/−9
- src/Monomer/Widgets/Singles/Label.hs +2/−2
- src/Monomer/Widgets/Util/Drawing.hs +19/−4
- src/Monomer/Widgets/Util/Widget.hs +0/−1
- test/unit/Monomer/Core/SizeReqSpec.hs +30/−0
- test/unit/Monomer/Graphics/UtilSpec.hs +1/−1
- test/unit/Monomer/TestUtil.hs +1/−0
- test/unit/Monomer/Widgets/Containers/BoxSpec.hs +1/−2
- test/unit/Monomer/Widgets/Containers/GridSpec.hs +1/−2
- test/unit/Monomer/Widgets/Containers/ScrollSpec.hs +40/−0
- test/unit/Monomer/Widgets/Containers/StackSpec.hs +12/−4
- test/unit/Spec.hs +6/−0
ChangeLog.md view
@@ -1,3 +1,16 @@+### 1.1.0.0++- Reduce memory usage by sharing wreq session among image widget instances.+- Add sizeUpdater helpers. Support multiple handlers in box, grid and stack.+- Add `dpr` field to `WidgetEnv`.+- Add `RunInRenderThread` to support initialization of low level OpenGL resources.+- Set correct `WidgetEnv` viewport from scroll (it looked good because of scissoring).+- Fix issue with scrollbars using child coordinates for detecting clicks.+- Add OpenGL example.+- Add `containerDrawDecorations`. Simplify button/externalLink internals.+- Add ThemeState entries for optionButton and toggleButton widgets.+- Add `singleDrawDecorations`. Make it consistent with Container.+ ### 1.0.0.3 - Consume and forward all available messages from Producers on each cycle.
README.md view
@@ -54,6 +54,7 @@ - [Books](docs/examples/02-books.md) - [Ticker](docs/examples/03-ticker.md) - [Generative](docs/examples/04-generative.md)+- [Custom OpenGL](docs/examples/05-opengl.md) ### Haddock
cbits/fontmanager.c view
@@ -2,6 +2,7 @@ #include <stdio.h> #include <stdlib.h>+#include <string.h> #include <memory.h> #include "fontstash.h"
examples/books/BookTypes.hs view
@@ -1,3 +1,13 @@+{-|+Module : BookTypes+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Types for the 'Books' example.+-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE ScopedTypeVariables #-}
examples/books/Main.hs view
@@ -1,3 +1,13 @@+{-|+Module : Main+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Main module for the 'Books' example.+-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE ScopedTypeVariables #-}@@ -14,6 +24,7 @@ import qualified Data.Text as T import qualified Network.Wreq as W+import qualified Network.Wreq.Session as Sess import BookTypes import Monomer@@ -123,16 +134,17 @@ ] handleEvent- :: WidgetEnv BooksModel BooksEvt+ :: Sess.Session+ -> WidgetEnv BooksModel BooksEvt -> WidgetNode BooksModel BooksEvt -> BooksModel -> BooksEvt -> [EventResponse BooksModel BooksEvt BooksModel ()]-handleEvent wenv node model evt = case evt of+handleEvent sess wenv node model evt = case evt of BooksInit -> [setFocusOnKey wenv "query"] BooksSearch -> [ Model $ model & searching .~ True,- Task $ searchBooks (model ^. query)+ Task $ searchBooks sess (model ^. query) ] BooksSearchResult resp -> [ Message "mainScroll" ScrollReset,@@ -151,8 +163,8 @@ BooksCloseDetails -> [Model $ model & selected .~ Nothing] BooksCloseError -> [Model $ model & errorMsg .~ Nothing] -searchBooks :: Text -> IO BooksEvt-searchBooks query = do+searchBooks :: Sess.Session -> Text -> IO BooksEvt+searchBooks sess query = do putStrLn . T.unpack $ "Searching: " <> query result <- catchAny (fetch url) (return . Left . T.pack . show) @@ -165,7 +177,7 @@ | null (resp ^. docs) = Nothing | otherwise = Just resp fetch url = do- resp <- W.get url+ resp <- Sess.get sess url >>= W.asJSON >>= return . preview (W.responseBody . _Just) @@ -173,7 +185,8 @@ main :: IO () main = do- startApp initModel handleEvent buildUI config+ sess <- Sess.newAPISession+ startApp initModel (handleEvent sess) buildUI config where config = [ appWindowTitle "Book search",
examples/generative/GenerativeTypes.hs view
@@ -1,3 +1,14 @@+{-|+Module : GenerativeTypes+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Types for the 'Generative' example.+-}+ {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-}
examples/generative/Main.hs view
@@ -1,3 +1,13 @@+{-|+Module : Main+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Main module for the 'Generative' example.+-} {-# LANGUAGE ScopedTypeVariables #-} module Main where
examples/generative/Widgets/BoxesPalette.hs view
@@ -1,3 +1,13 @@+{-|+Module : Widgets.BoxesPalette+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Widget representing a set of boxes with varying colors.+-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-}
examples/generative/Widgets/CirclesGrid.hs view
@@ -1,3 +1,13 @@+{-|+Module : Widgets.CirclesGrid+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Widget representing a grid of circles.+-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-}
+ examples/opengl/Main.hs view
@@ -0,0 +1,101 @@+{-|+Module : Main+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Main module for the 'Custom OpenGL' example.+-}+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell #-}++module Main where++import Control.Lens+import Data.Text (Text)+import Monomer++import qualified Data.Map as M+import qualified Monomer.Lens as L++import OpenGLWidget++data AppModel = AppModel {+ _color1 :: Color,+ _color2 :: Color,+ _color3 :: Color,+ _color4 :: Color+} deriving (Eq, Show)++data AppEvent+ = AppInit+ deriving (Eq, Show)++makeLenses 'AppModel++buildUI+ :: WidgetEnv AppModel AppEvent+ -> AppModel+ -> WidgetNode AppModel AppEvent+buildUI wenv model = widgetTree where+ colorsMap = M.fromList [(red, "Red"), (green, "Green"), (blue, "Blue"), (orange, "Orange")]+ colors = M.keys colorsMap+ colorDropdown field = textDropdown_ field colors (colorsMap M.!) []++ widgetTree = vstack [+ hstack [+ label "Color 1:",+ spacer,+ colorDropdown color1,+ spacer,+ label "Color 2:",+ spacer,+ colorDropdown color2,+ spacer,+ label "Color 3:",+ spacer,+ colorDropdown color3,+ spacer,+ label "Color 4:",+ spacer,+ colorDropdown color4+ ],+ spacer,+ vgrid [+ hgrid [+ openGLWidget (model ^. color1)+ `styleBasic` [padding 20],+ scroll (openGLWidget (model ^. color2) `styleBasic` [width 800, height 800])+ `styleBasic` [padding 20]+ ],+ hgrid [+ openGLWidget (model ^. color3)+ `styleBasic` [padding 20],+ openGLWidget (model ^. color4)+ `styleBasic` [padding 20]+ ]+ ]+ ] `styleBasic` [padding 10]++handleEvent+ :: WidgetEnv AppModel AppEvent+ -> WidgetNode AppModel AppEvent+ -> AppModel+ -> AppEvent+ -> [AppEventResponse AppModel AppEvent]+handleEvent wenv node model evt = case evt of+ AppInit -> []++main :: IO ()+main = do+ startApp model handleEvent buildUI config+ where+ config = [+ appWindowTitle "OpenGL",+ appTheme darkTheme,+ appFontDef "Regular" "./assets/fonts/Roboto-Regular.ttf",+ appInitEvent AppInit+ ]+ model = AppModel red green blue orange
+ examples/opengl/OpenGLWidget.hs view
@@ -0,0 +1,239 @@+{-|+Module : Main+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Widget implementing low level rendering for the 'Custom OpenGL' example.++OpenGL code adapted from dmjio's [example](https://github.com/dmjio/LearnOpenGL.hs/blob/master/GettingStarted/ShadersEx2/Main.hs).+-}+{-# LANGUAGE ExistentialQuantification #-}+{-# LANGUAGE FlexibleContexts #-}++module OpenGLWidget (+ openGLWidget+) where++import Control.Lens ((&), (^.), (.~))+import Control.Monad+import Data.Default+import Data.Typeable (cast)+import Data.Vector.Storable (Vector)+import Foreign.C.String+import Foreign.Marshal.Alloc+import Foreign.Ptr+import Foreign.Storable+import Graphics.GL++import qualified Data.Vector.Storable as V++import Monomer+import Monomer.Widgets.Single++import qualified Monomer.Lens as L++data OpenGLWidgetMsg+ = OpenGLWidgetInit GLuint (Ptr GLuint) (Ptr GLuint)+ deriving (Show, Eq)++data OpenGLWidgetState = OpenGLWidgetState {+ _ogsLoaded :: Bool,+ _ogsShaderId :: GLuint,+ _ogsVao :: Ptr GLuint,+ _ogsVbo :: Ptr GLuint+} deriving (Show, Eq)++openGLWidget :: Color -> WidgetNode s e+openGLWidget color = defaultWidgetNode "openGLWidget" widget where+ widget = makeOpenGLWidget color state+ state = OpenGLWidgetState False 0 nullPtr nullPtr++makeOpenGLWidget :: Color -> OpenGLWidgetState -> Widget s e+makeOpenGLWidget color state = widget where+ widget = createSingle state def {+ singleInit = init,+ singleMerge = merge,+ singleDispose = dispose,+ singleHandleMessage = handleMessage,+ singleGetSizeReq = getSizeReq,+ singleRender = render+ }++ init wenv node = resultReqs node reqs where+ widgetId = node ^. L.info . L.widgetId+ path = node ^. L.info . L.path+ buffers = 2++ initOpenGL = do+ -- This needs to run in render thread+ program <- createShaderProgram+ vaoPtr <- malloc+ vboPtr <- malloc+ glGenVertexArrays buffers vaoPtr+ glGenBuffers buffers vboPtr++ return $ OpenGLWidgetInit program vaoPtr vboPtr++ reqs = [RunInRenderThread widgetId path initOpenGL]++ merge wenv node oldNode oldState = resultNode newNode where+ newNode = node+ & L.widget .~ makeOpenGLWidget color oldState++ dispose wenv node = resultReqs node reqs where+ OpenGLWidgetState _ shaderId vaoPtr vboPtr = state+ widgetId = node ^. L.info . L.widgetId+ path = node ^. L.info . L.path+ buffers = 2++ disposeOpenGL = do+ -- This needs to run in render thread+ glDeleteProgram shaderId+ glDeleteVertexArrays buffers vaoPtr+ glDeleteBuffers buffers vboPtr+ free vaoPtr+ free vboPtr++ reqs = [RunInRenderThread widgetId path disposeOpenGL]++ handleMessage wenv node target msg = case cast msg of+ Just (OpenGLWidgetInit shaderId vao vbo) -> Just result where+ newState = OpenGLWidgetState True shaderId vao vbo+ newNode = node+ & L.widget .~ makeOpenGLWidget color newState+ result = resultReqs newNode [RenderOnce]+ _ -> Nothing++ getSizeReq wenv node = (sizeReqW, sizeReqH) where+ sizeReqW = expandSize 100 1+ sizeReqH = expandSize 100 1++ render wenv node renderer =+ when (_ogsLoaded state) $+ createRawTask renderer $+ doInScissor winSize dpr offset activeVp $+ drawVertices state (toVectorVAO winSize offset color triangle)+ where+ dpr = wenv ^. L.dpr+ winSize = wenv ^. L.windowSize+ activeVp = wenv ^. L.viewport+ offset = wenv ^. L.offset++ -- Simple triangle+ style = currentStyle wenv node+ nodeVp = getContentArea node style++ Rect rx ry rw rh = nodeVp+ triangle = [(rx + rw, ry + rh), (rx, ry + rh), (rx + rw / 2, ry)]++doInScissor :: Size -> Double -> Point -> Rect -> IO () -> IO ()+doInScissor winSize dpr offset vp action = do+ glEnable GL_SCISSOR_TEST+ -- OpenGL's Y axis increases from bottom to top+ glScissor (round (rx + ox)) (round $ winH - ry - oy - rh) (round rw) (round rh)+ action+ glDisable GL_SCISSOR_TEST+ where+ winH = winSize ^. L.h * dpr+ Point ox oy = mulPoint dpr offset+ Rect rx ry rw rh = mulRect dpr vp++toVectorVAO :: Size -> Point -> Color -> [(Double, Double)] -> Vector Float+toVectorVAO (Size w h) (Point ox oy) (Color r g b a) points = vec where+ px x = realToFrac $ (x + ox - w / 2) / (w / 2)+ -- OpenGL's Y axis increases from bottom to top+ py y = realToFrac $ (h / 2 - y - oy) / (h / 2)+ col c = realToFrac (fromIntegral c / 255)+ row (x, y) = [px x, py y, 0, col r, col g, col b]+ vec = V.fromList . concat $ row <$> points++drawVertices+ :: forall a . Storable a+ => OpenGLWidgetState+ -> Vector a+ -> IO ()+drawVertices state vertices = do+ vao <- peek vaoPtr+ vbo <- peek vboPtr++ glBindVertexArray vao+ glBindBuffer GL_ARRAY_BUFFER vbo++ -- Copies raw data from vector to OpenGL memory+ V.unsafeWith vertices $ \vertsPtr ->+ glBufferData+ GL_ARRAY_BUFFER+ (fromIntegral (V.length vertices * floatSize))+ (castPtr vertsPtr)+ GL_STATIC_DRAW++ -- The vertex shader expects two arguments. Position:+ glVertexAttribPointer 0 3 GL_FLOAT GL_FALSE (fromIntegral (floatSize * 6)) nullPtr+ glEnableVertexAttribArray 0++ -- Color:+ glVertexAttribPointer 1 3 GL_FLOAT GL_FALSE (fromIntegral (floatSize * 6)) (nullPtr `plusPtr` (floatSize * 3))+ glEnableVertexAttribArray 1++ glUseProgram shaderId+ glBindVertexArray vao+ glDrawArrays GL_TRIANGLES 0 3++ where+ floatSize = sizeOf (undefined :: Float)+ OpenGLWidgetState _ shaderId vaoPtr vboPtr = state++createShaderProgram :: IO GLuint+createShaderProgram = do+ shaderProgram <- glCreateProgram+ vertexShader <- compileShader GL_VERTEX_SHADER "examples/opengl/shaders/vert.glsl"+ fragmentShader <- compileShader GL_FRAGMENT_SHADER "examples/opengl/shaders/frag.glsl"++ glAttachShader shaderProgram vertexShader+ glAttachShader shaderProgram fragmentShader++ glLinkProgram shaderProgram+ checkProgramLink shaderProgram++ glDeleteShader vertexShader+ glDeleteShader fragmentShader++ return shaderProgram++compileShader :: GLenum -> FilePath -> IO GLuint+compileShader shaderType shaderFile = do+ shader <- glCreateShader shaderType+ shaderSource <- readFile shaderFile >>= newCString++ alloca $ \shadersStr -> do+ shadersStr `poke` shaderSource+ glShaderSource shader 1 shadersStr nullPtr+ glCompileShader shader+ checkShaderCompile shader++ return shader++checkProgramLink :: GLuint -> IO ()+checkProgramLink programId = do+ alloca $ \successPtr -> do+ alloca $ \infoLogPtr -> do+ glGetProgramiv programId GL_LINK_STATUS successPtr+ success <- peek successPtr++ when (success <= 0) $ do+ glGetProgramInfoLog programId 512 nullPtr infoLogPtr+ putStrLn =<< peekCString infoLogPtr++checkShaderCompile :: GLuint -> IO ()+checkShaderCompile shaderId = do+ alloca $ \successPtr ->+ alloca $ \infoLogPtr -> do+ glGetShaderiv shaderId GL_COMPILE_STATUS successPtr+ success <- peek successPtr++ when (success <= 0) $ do+ glGetShaderInfoLog shaderId 512 nullPtr infoLogPtr+ putStrLn "Failed to compile shader "
examples/ticker/BinanceTypes.hs view
@@ -1,3 +1,13 @@+{-|+Module : BinanceTypes+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Types for the Binance Exchange in the 'Ticker' example.+-} {-# LANGUAGE DeriveAnyClass #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-}
examples/ticker/Main.hs view
@@ -1,3 +1,13 @@+{-|+Module : Main+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Main module for the 'Ticker' example.+-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-}
examples/ticker/TickerTypes.hs view
@@ -1,3 +1,13 @@+{-|+Module : Main+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Types for the 'Ticker' example.+-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE RecordWildCards #-}
examples/todo/Main.hs view
@@ -1,3 +1,13 @@+{-|+Module : Main+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Main module for the 'Todo' example.+-} {-# LANGUAGE BinaryLiterals #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-}
examples/todo/TodoTypes.hs view
@@ -1,3 +1,13 @@+{-|+Module : TodoTypes+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Types for the 'Todo' example.+-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TemplateHaskell #-}
examples/tutorial/Main.hs view
@@ -1,3 +1,13 @@+{-|+Module : Main+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Main module for the tutorials.+-} module Main where import qualified Tutorial01_Basics
examples/tutorial/Tutorial01_Basics.hs view
@@ -1,3 +1,13 @@+{-|+Module : Tutorial01_Basics+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Main module for the '01 - Basics' tutorial.+-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-}
examples/tutorial/Tutorial02_Styling.hs view
@@ -1,3 +1,13 @@+{-|+Module : Tutorial02_Styling+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Main module for the '02 - Styling' tutorial.+-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-}
examples/tutorial/Tutorial03_LifeCycle.hs view
@@ -1,3 +1,13 @@+{-|+Module : Tutorial03_LifeCycle+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Main module for the '03 - LifeCycle' tutorial.+-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-}
examples/tutorial/Tutorial04_Tasks.hs view
@@ -1,3 +1,13 @@+{-|+Module : Tutorial04_Tasks+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Main module for the '04 - Tasks' tutorial.+-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-}
examples/tutorial/Tutorial05_Producers.hs view
@@ -1,3 +1,13 @@+{-|+Module : Tutorial05_Producers+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Main module for the '05 - Producers' tutorial.+-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-}
examples/tutorial/Tutorial06_Composite.hs view
@@ -1,3 +1,13 @@+{-|+Module : Tutorial06_Composite+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Main module for the '06 - Composite' tutorial.+-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-}
examples/tutorial/Tutorial07_CustomWidget.hs view
@@ -1,3 +1,13 @@+{-|+Module : Tutorial07_CustomWidget+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Main module for the '07 - Custom Widget' tutorial.+-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-}
examples/tutorial/Tutorial08_Themes.hs view
@@ -1,3 +1,13 @@+{-|+Module : Tutorial08_Themes+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Main module for the '08 - Themes' tutorial.+-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE TemplateHaskell #-}
monomer.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack name: monomer-version: 1.0.0.3+version: 1.1.0.0 synopsis: A GUI library for writing native Haskell applications. description: Monomer is an easy to use, cross platform, GUI library for writing native Haskell applications.@@ -274,6 +274,49 @@ , wreq >=0.5.2 && <0.6 default-language: Haskell2010 +executable opengl+ main-is: Main.hs+ other-modules:+ OpenGLWidget+ Paths_monomer+ hs-source-dirs:+ examples/opengl+ default-extensions:+ OverloadedStrings+ ghc-options: -threaded+ build-depends:+ JuicyPixels >=3.2.9 && <3.5+ , OpenGL ==3.0.*+ , OpenGLRaw ==3.3.*+ , async >=2.1 && <2.3+ , attoparsec >=0.12 && <0.15+ , base >=4.11 && <5+ , bytestring >=0.10 && <0.12+ , bytestring-to-vector ==0.3.*+ , containers >=0.5.11 && <0.7+ , data-default >=0.5 && <0.8+ , exceptions ==0.10.*+ , extra >=1.6 && <1.9+ , formatting >=6.0 && <8.0+ , http-client >=0.6 && <0.9+ , lens >=4.16 && <5.1+ , monomer+ , mtl >=2.1 && <2.3+ , nanovg >=0.8 && <1.0+ , process ==1.6.*+ , random >=1.1 && <1.3+ , safe ==0.3.*+ , sdl2 >=2.4.0 && <2.6+ , stm ==2.5.*+ , text ==1.2.*+ , text-show >=3.7 && <3.10+ , time >=1.8 && <1.13+ , transformers >=0.5 && <0.7+ , unordered-containers >=0.2.8 && <0.3+ , vector >=0.12 && <0.14+ , wreq >=0.5.2 && <0.6+ default-language: Haskell2010+ executable ticker main-is: Main.hs other-modules:@@ -415,6 +458,7 @@ main-is: Spec.hs other-modules: Monomer.Common.CursorIconSpec+ Monomer.Core.SizeReqSpec Monomer.Graphics.UtilSpec Monomer.TestEventUtil Monomer.TestUtil
src/Monomer/Common/BasicTypes.hs view
@@ -131,6 +131,10 @@ moveRect :: Point -> Rect -> Rect moveRect (Point x y) (Rect rx ry rw rh) = Rect (rx + x) (ry + y) rw rh +-- | Scales a rect by the provided factor.+mulRect :: Double -> Rect -> Rect+mulRect f (Rect rx ry rw rh) = Rect (f * rx) (f * ry) (f * rw) (f * rh)+ -- | Returns the middle point of a rect. rectCenter :: Rect -> Point rectCenter (Rect rx ry rw rh) = Point (rx + rw / 2) (ry + rh / 2)
src/Monomer/Core/Combinators.hs view
@@ -23,7 +23,6 @@ import Control.Lens (ALens') import Data.Text (Text) -import Monomer.Common import Monomer.Core.StyleTypes import Monomer.Core.WidgetTypes import Monomer.Event.Types
src/Monomer/Core/SizeReq.hs view
@@ -13,6 +13,12 @@ module Monomer.Core.SizeReq ( SizeReqUpdater(..), clearExtra,+ fixedToMinW,+ fixedToMinH,+ fixedToMaxW,+ fixedToMaxH,+ fixedToExpandW,+ fixedToExpandH, sizeReqBounded, sizeReqValid, sizeReqAddStyle,@@ -43,9 +49,51 @@ -- | Transforms a SizeReq pair by applying an arbitrary operation. type SizeReqUpdater = (SizeReq, SizeReq) -> (SizeReq, SizeReq) --- | Clears the extra field of a SizeReq.+-- | Clears the extra field of a pair of SizeReqs. clearExtra :: SizeReqUpdater-clearExtra (req1, req2) = (req1 & L.extra .~ 0, req2 & L.extra .~ 0)+clearExtra (reqW, reqH) = (reqW & L.extra .~ 0, reqH & L.extra .~ 0)++-- | Switches a SizeReq pair from fixed width to minimum width.+fixedToMinW+ :: Double -- ^ The resize factor.+ -> SizeReqUpdater -- ^ The updated SizeReq.+fixedToMinW fw (SizeReq fixed _ _ _, reqH) = (newReqH, reqH) where+ newReqH = SizeReq fixed 0 fixed fw++-- | Switches a SizeReq pair from fixed height to minimum height.+fixedToMinH+ :: Double -- ^ The resize factor.+ -> SizeReqUpdater -- ^ The updated SizeReq.+fixedToMinH fh (reqW, SizeReq fixed _ _ _) = (reqW, newReqH) where+ newReqH = SizeReq fixed 0 fixed fh++-- | Switches a SizeReq pair from fixed width to maximum width.+fixedToMaxW+ :: Double -- ^ The resize factor.+ -> SizeReqUpdater -- ^ The updated SizeReq.+fixedToMaxW fw (SizeReq fixed _ _ _, reqH) = (newReqH, reqH) where+ newReqH = SizeReq 0 fixed 0 fw++-- | Switches a SizeReq pair from fixed height to maximum height.+fixedToMaxH+ :: Double -- ^ The resize factor.+ -> SizeReqUpdater -- ^ The updated SizeReq.+fixedToMaxH fh (reqW, SizeReq fixed _ _ _) = (reqW, newReqH) where+ newReqH = SizeReq 0 fixed 0 fh++-- | Switches a SizeReq pair from fixed width to expand width.+fixedToExpandW+ :: Double -- ^ The resize factor.+ -> SizeReqUpdater -- ^ The updated SizeReq.+fixedToExpandW fw (SizeReq fixed _ _ _, reqH) = (newReqH, reqH) where+ newReqH = SizeReq 0 fixed fixed fw++-- | Switches a SizeReq pair from fixed height to expand height.+fixedToExpandH+ :: Double -- ^ The resize factor.+ -> SizeReqUpdater -- ^ The updated SizeReq.+fixedToExpandH fh (reqW, SizeReq fixed _ _ _) = (reqW, newReqH) where+ newReqH = SizeReq 0 fixed fixed fh -- | Returns a bounded value by the SizeReq, starting from value and offset. sizeReqBounded :: SizeReq -> Double -> Double -> Double
src/Monomer/Core/ThemeTypes.hs view
@@ -74,6 +74,8 @@ _thsExternalLinkStyle :: StyleState, _thsLabelStyle :: StyleState, _thsNumericFieldStyle :: StyleState,+ _thsOptionBtnOnStyle :: StyleState,+ _thsOptionBtnOffStyle :: StyleState, _thsRadioStyle :: StyleState, _thsRadioWidth :: Double, _thsScrollOverlay :: Bool,@@ -97,6 +99,8 @@ _thsTextAreaStyle :: StyleState, _thsTextFieldStyle :: StyleState, _thsTimeFieldStyle :: StyleState,+ _thsToggleBtnOnStyle :: StyleState,+ _thsToggleBtnOffStyle :: StyleState, _thsTooltipStyle :: StyleState, _thsUserStyleMap :: M.Map String StyleState, _thsUserColorMap :: M.Map String Color@@ -126,6 +130,8 @@ _thsExternalLinkStyle = def, _thsLabelStyle = def, _thsNumericFieldStyle = def,+ _thsOptionBtnOnStyle = def,+ _thsOptionBtnOffStyle = def, _thsRadioStyle = def, _thsRadioWidth = 20, _thsScrollOverlay = False,@@ -149,6 +155,8 @@ _thsTextAreaStyle = def, _thsTextFieldStyle = def, _thsTimeFieldStyle = def,+ _thsToggleBtnOnStyle = def,+ _thsToggleBtnOffStyle = def, _thsTooltipStyle = def, _thsUserStyleMap = M.empty, _thsUserColorMap = M.empty@@ -178,6 +186,8 @@ _thsExternalLinkStyle = _thsExternalLinkStyle t1 <> _thsExternalLinkStyle t2, _thsLabelStyle = _thsLabelStyle t1 <> _thsLabelStyle t2, _thsNumericFieldStyle = _thsNumericFieldStyle t1 <> _thsNumericFieldStyle t2,+ _thsOptionBtnOnStyle = _thsOptionBtnOnStyle t1 <> _thsOptionBtnOnStyle t2,+ _thsOptionBtnOffStyle = _thsOptionBtnOffStyle t1 <> _thsOptionBtnOffStyle t2, _thsRadioStyle = _thsRadioStyle t1 <> _thsRadioStyle t2, _thsRadioWidth = _thsRadioWidth t2, _thsScrollOverlay = _thsScrollOverlay t2,@@ -201,6 +211,8 @@ _thsTextAreaStyle = _thsTextAreaStyle t1 <> _thsTextAreaStyle t2, _thsTextFieldStyle = _thsTextFieldStyle t1 <> _thsTextFieldStyle t2, _thsTimeFieldStyle = _thsTimeFieldStyle t1 <> _thsTimeFieldStyle t2,+ _thsToggleBtnOnStyle = _thsToggleBtnOnStyle t1 <> _thsToggleBtnOnStyle t2,+ _thsToggleBtnOffStyle = _thsToggleBtnOffStyle t1 <> _thsToggleBtnOffStyle t2, _thsTooltipStyle = _thsTooltipStyle t1 <> _thsTooltipStyle t2, _thsUserStyleMap = _thsUserStyleMap t1 <> _thsUserStyleMap t2, _thsUserColorMap = _thsUserColorMap t1 <> _thsUserColorMap t2
src/Monomer/Core/WidgetTypes.hs view
@@ -18,7 +18,6 @@ import Control.Concurrent (MVar) import Control.Lens (ALens')-import Data.ByteString.Lazy (ByteString) import Data.Default import Data.Map.Strict (Map) import Data.Sequence (Seq)@@ -226,6 +225,11 @@ -- for WebSockets and similar data sources. It receives a function that -- can be used to send messages back to the producer owner. | forall i . Typeable i => RunProducer WidgetId Path ((i -> IO ()) -> IO ())+ -- | Runs an asynchronous tasks in the render thread. It is mandatory to+ -- return a message that will be sent to the task owner (this is the only+ -- way to feed data back). This should only be used when implementing low+ -- level rendering widgets that need to create API specific resources.+ | forall i . Typeable i => RunInRenderThread WidgetId Path (IO i) instance Eq e => Eq (WidgetRequest s e) where IgnoreParentEvents == IgnoreParentEvents = True@@ -287,6 +291,8 @@ data WidgetEnv s e = WidgetEnv { -- | The OS of the host. _weOs :: Text,+ -- | Device pixel rate.+ _weDpr :: Double, -- | Provides helper funtions for calculating text size. _weFontManager :: FontManager, -- | Returns the information of a node given a path from root, if any.@@ -704,6 +710,7 @@ show SendMessage{} = "SendMessage" show RunTask{} = "RunTask" show RunProducer{} = "RunProducer"+ show RunInRenderThread{} = "RunInRenderThread" instance Show (WidgetResult s e) where show result = "WidgetResult "
src/Monomer/Graphics/Types.hs view
@@ -40,7 +40,7 @@ _colorG :: {-# UNPACK #-} !Int, _colorB :: {-# UNPACK #-} !Int, _colorA :: {-# UNPACK #-} !Double-} deriving (Eq, Show, Generic)+} deriving (Eq, Show, Ord, Generic) instance Default Color where def = Color 255 255 255 1.0
src/Monomer/Helper.hs view
@@ -11,6 +11,7 @@ -} module Monomer.Helper where +import Control.Exception (SomeException, catch) import Control.Monad.IO.Class (MonadIO) import Data.Sequence (Seq(..)) @@ -48,6 +49,10 @@ Just val -> val :<| seqCatMaybes xs _ -> seqCatMaybes xs +-- | Folds a list of functions over an initial value.+applyFnList :: [a -> a] -> a -> a+applyFnList fns initial = foldl (flip ($)) initial fns+ -- | Returns the maximum value of a given floating type. maxNumericValue :: (RealFloat a) => a maxNumericValue = x where@@ -59,3 +64,7 @@ -- | Restricts a value to a given range. clamp :: (Ord a) => a -> a -> a -> a clamp mn mx = max mn . min mx++-- | Catches any exception thrown by the provided action+catchAny :: IO a -> (SomeException -> IO a) -> IO a+catchAny = catch
src/Monomer/Main/Core.hs view
@@ -47,6 +47,7 @@ import Monomer.Main.Util import Monomer.Main.WidgetTask import Monomer.Graphics+import Monomer.Helper (catchAny) import Monomer.Widgets.Composite import qualified Monomer.Lens as L@@ -152,6 +153,7 @@ let wenv = WidgetEnv { _weOs = os,+ _weDpr = dpr, _weFontManager = fontManager, _weFindByPath = const Nothing, _weMainButton = mainBtn,@@ -258,6 +260,7 @@ let contextBtn = fromMaybe BtnRight (_apcContextButton config) let wenv = WidgetEnv { _weOs = _mlOS,+ _weDpr = dpr, _weFontManager = fontManager, _weFindByPath = findWidgetByPath wenv _mlWidgetRoot, _weMainButton = mainBtn,@@ -409,6 +412,11 @@ return state handleRenderMsg window renderer fontMgr state (MsgRemoveImage name) = do deleteImage renderer name+ return state+handleRenderMsg window renderer fontMgr state (MsgRunInRender chan task) = do+ flip catchAny print $ do+ value <- task+ atomically $ writeTChan chan value return state renderWidgets
src/Monomer/Main/Handlers.hs view
@@ -29,7 +29,7 @@ import Control.Lens ((&), (^.), (^?), (.~), (?~), (%~), (.=), (?=), (%=), (%%~), _Just, _1, _2, ix, at, use) import Control.Monad.STM (atomically)-import Control.Concurrent.STM.TChan (TChan, newTChanIO, writeTChan)+import Control.Concurrent.STM.TChan (TChan, newTChanIO, readTChan, writeTChan) import Control.Applicative ((<|>)) import Control.Monad import Control.Monad.IO.Class@@ -251,6 +251,7 @@ SendMessage wid msg -> handleSendMessage wid msg step RunTask wid path handler -> handleRunTask wid path handler step RunProducer wid path handler -> handleRunProducer wid path handler step+ RunInRenderThread wid path handler -> handleRunInRenderThread wid path handler step -- | Resizes the current root, and marks the render and resized flags. handleResizeWidgets@@ -653,6 +654,23 @@ return previousStep +handleRunInRenderThread+ :: forall s e m i . (MonomerM s e m, Typeable i)+ => WidgetId+ -> Path+ -> IO i+ -> HandlerStep s e+ -> m (HandlerStep s e)+handleRunInRenderThread widgetId path handler previousStep = do+ renderChannel <- use L.renderChannel++ handleRunTask widgetId path (taskWrapper renderChannel) previousStep+ where+ taskWrapper renderChannel = do+ msgChan <- newTChanIO+ atomically $ writeTChan renderChannel (MsgRunInRender msgChan handler)+ atomically $ readTChan msgChan+ sendMessage :: TChan e -> e -> IO () sendMessage channel message = atomically $ writeTChan channel message @@ -690,9 +708,7 @@ Move point -> do (target, hoverEvts) <- addHoverEvents wenv widgetRoot point -- Update input status- status <- use L.inputStatus- L.inputStatus . L.mousePosPrev .= status ^. L.mousePos- L.inputStatus . L.mousePos .= point+ updateInputStatusMousePos point -- Drag event mainPress <- use L.mainBtnPress draggedMsg <- getDraggedMsgInfo@@ -716,6 +732,7 @@ when (btn == mainBtn) $ L.mainBtnPress .= fmap (, point) curr + updateInputStatusMousePos point L.inputStatus . L.buttons . at btn ?= BtnPressed SDLE.captureMouse True@@ -739,6 +756,7 @@ Just (path, msg) -> [(Drop point path msg, target) | not isPressed] _ -> [] + updateInputStatusMousePos point L.inputStatus . L.buttons . at btn ?= BtnReleased SDLE.captureMouse False@@ -753,6 +771,13 @@ -- This will only be reached from `handleSystemEvents` Click point btn clicks -> findEvtTargetByPoint wenv widgetRoot evt point _ -> return [(evt, Nothing)]++updateInputStatusMousePos :: MonomerM s e m => Point -> m ()+updateInputStatusMousePos point = do+ -- Update input status+ status <- use L.inputStatus+ L.inputStatus . L.mousePosPrev .= status ^. L.mousePos+ L.inputStatus . L.mousePos .= point addHoverEvents :: MonomerM s e m
src/Monomer/Main/Types.hs view
@@ -50,7 +50,7 @@ = MsgRender (WidgetEnv s e) (WidgetNode s e) | MsgResize Size | MsgRemoveImage Text- deriving Show+ | forall i . MsgRunInRender (TChan i) (IO i) {-| Requirements for periodic rendering by a widget. Start time is stored to
src/Monomer/Widgets/Composite.hs view
@@ -70,7 +70,6 @@ import Data.Sequence (Seq(..), (|>), (<|), fromList) import Data.Typeable (Typeable, cast, typeOf) -import qualified Data.ByteString.Lazy as BSL import qualified Data.Map.Strict as M import qualified Data.Sequence as Seq @@ -867,6 +866,7 @@ toParentReq _ (SendMessage wid message) = Just (SendMessage wid message) toParentReq _ (RunTask wid path action) = Just (RunTask wid path action) toParentReq _ (RunProducer wid path action) = Just (RunProducer wid path action)+toParentReq _ (RunInRenderThread wid path action) = Just (RunInRenderThread wid path action) collectWidgetKeys :: Map WidgetKey (WidgetNode s e)@@ -883,6 +883,7 @@ convertWidgetEnv :: WidgetEnv sp ep -> WidgetKeyMap s e -> s -> WidgetEnv s e convertWidgetEnv wenv widgetKeyMap model = WidgetEnv { _weOs = _weOs wenv,+ _weDpr = _weDpr wenv, _weFontManager = _weFontManager wenv, _weFindByPath = _weFindByPath wenv, _weMainButton = _weMainButton wenv,
src/Monomer/Widgets/Container.hs view
@@ -329,6 +329,9 @@ -- | Scissor to apply to child widgets. This is not the same as the widget -- enabled by containerUseScissor containerChildrenScissor :: Maybe Rect,+ -- | If True, the container will render its background and border. In some+ -- cases passing rendering control to children is useful. Defaults to True.+ containerDrawDecorations :: Bool, -- | The layout direction generated by this widget. If one is indicated, it -- can be used by widgets such as "Monomer.Widgets.Singles.Spacer" containerLayoutDirection :: LayoutDirection,@@ -391,6 +394,7 @@ containerAddStyleReq = True, containerChildrenOffset = Nothing, containerChildrenScissor = Nothing,+ containerDrawDecorations = True, containerLayoutDirection = LayoutNone, containerIgnoreEmptyArea = False, containerUseCustomCursor = False,@@ -471,11 +475,17 @@ cOffset = containerChildrenOffset container updateCWenv = containerUpdateCWenv container layoutDirection = containerLayoutDirection container++ pViewport = node ^. L.info . L.viewport+ cViewport = cnode ^. L.info . L.viewport+ newViewport = fromMaybe def (intersectRects pViewport cViewport)+ offsetWenv !wenv- | isJust cOffset = updateWenvOffset container wenv node+ | isJust cOffset = updateWenvOffset container wenv node newViewport | otherwise = wenv !directionWenv = wenv & L.layoutDirection .~ layoutDirection+ !newWenv = updateCWenv (offsetWenv directionWenv) node cnode cidx {-|@@ -487,15 +497,16 @@ :: Container s e a -- ^ The container config -> WidgetEnv s e -- ^ The widget environment. -> WidgetNode s e -- ^ The widget node.+ -> Rect -- ^ The target viewport. -> WidgetEnv s e -- ^ THe updated widget environment.-updateWenvOffset container wenv node = newWenv where+updateWenvOffset container wenv node viewport = newWenv where cOffset = containerChildrenOffset container offset = fromMaybe def cOffset- accumOffset = addPoint offset (wenv ^. L.offset)- viewport = node ^. L.info . L.viewport+ updateMain (path, point) | isNodeParentOfPath node path = (path, addPoint (negPoint offset) point) | otherwise = (path, point)+ newWenv = wenv & L.viewport .~ moveRect (negPoint offset) viewport & L.inputStatus . L.mousePos %~ addPoint (negPoint offset)@@ -1089,7 +1100,7 @@ -> IO () renderWrapper container wenv !node !renderer = drawInScissor renderer useScissor viewport $- drawStyledAction renderer viewport style $ \_ -> do+ drawStyledAction_ renderer drawDecorations viewport style $ \_ -> do renderBefore wenv node renderer drawInScissor renderer useChildrenScissor childrenScissorRect $ do@@ -1109,6 +1120,7 @@ where style = containerGetCurrentStyle container wenv node updateCWenv = getUpdateCWenv container+ drawDecorations = containerDrawDecorations container useScissor = containerUseScissor container childrenScissor = containerChildrenScissor container offset = containerChildrenOffset container
src/Monomer/Widgets/Containers/Box.hs view
@@ -42,6 +42,7 @@ import qualified Data.Sequence as Seq +import Monomer.Helper (applyFnList) import Monomer.Widgets.Container import Monomer.Widgets.Containers.Stack @@ -78,7 +79,7 @@ data BoxCfg s e = BoxCfg { _boxExpandContent :: Maybe Bool, _boxIgnoreEmptyArea :: Maybe Bool,- _boxSizeReqUpdater :: Maybe SizeReqUpdater,+ _boxSizeReqUpdater :: [SizeReqUpdater], _boxMergeRequired :: Maybe (s -> s -> Bool), _boxAlignH :: Maybe AlignH, _boxAlignV :: Maybe AlignV,@@ -96,7 +97,7 @@ def = BoxCfg { _boxExpandContent = Nothing, _boxIgnoreEmptyArea = Nothing,- _boxSizeReqUpdater = Nothing,+ _boxSizeReqUpdater = [], _boxMergeRequired = Nothing, _boxAlignH = Nothing, _boxAlignV = Nothing,@@ -114,7 +115,7 @@ (<>) t1 t2 = BoxCfg { _boxExpandContent = _boxExpandContent t2 <|> _boxExpandContent t1, _boxIgnoreEmptyArea = _boxIgnoreEmptyArea t2 <|> _boxIgnoreEmptyArea t1,- _boxSizeReqUpdater = _boxSizeReqUpdater t2 <|> _boxSizeReqUpdater t1,+ _boxSizeReqUpdater = _boxSizeReqUpdater t1 <> _boxSizeReqUpdater t2, _boxMergeRequired = _boxMergeRequired t2 <|> _boxMergeRequired t1, _boxAlignH = _boxAlignH t2 <|> _boxAlignH t1, _boxAlignV = _boxAlignV t2 <|> _boxAlignV t1,@@ -138,7 +139,7 @@ instance CmbSizeReqUpdater (BoxCfg s e) where sizeReqUpdater updater = def {- _boxSizeReqUpdater = Just updater+ _boxSizeReqUpdater = [updater] } instance CmbMergeRequired (BoxCfg s e) s where@@ -384,11 +385,11 @@ getSizeReq :: ContainerGetSizeReqHandler s e getSizeReq wenv node children = newSizeReq where- updateSizeReq = fromMaybe id (_boxSizeReqUpdater config)+ sizeReqFns = _boxSizeReqUpdater config child = Seq.index children 0 newReqW = child ^. L.info . L.sizeReqW newReqH = child ^. L.info . L.sizeReqH- newSizeReq = updateSizeReq (newReqW, newReqH)+ newSizeReq = applyFnList sizeReqFns (newReqW, newReqH) resize wenv node viewport children = resized where style = getCurrentStyle wenv node
src/Monomer/Widgets/Containers/Dropdown.hs view
@@ -496,7 +496,7 @@ scOffset = wenv ^. L.offset offset = _ddsOffset state totalOffset = addPoint scOffset offset- cwenv = updateWenvOffset container wenv node+ cwenv = updateWenvOffset container wenv node listOverlayVp & L.viewport .~ listOverlayVp renderArrow renderer style contentArea =
src/Monomer/Widgets/Containers/Grid.hs view
@@ -32,6 +32,7 @@ import qualified Data.Sequence as Seq +import Monomer.Helper (applyFnList) import Monomer.Widgets.Container import qualified Monomer.Lens as L@@ -42,17 +43,17 @@ - 'sizeReqUpdater': allows modifying the 'SizeReq' generated by the grid. -} newtype GridCfg = GridCfg {- _grcSizeReqUpdater :: Maybe SizeReqUpdater+ _grcSizeReqUpdater :: [SizeReqUpdater] } instance Default GridCfg where def = GridCfg {- _grcSizeReqUpdater = Nothing+ _grcSizeReqUpdater = [] } instance Semigroup GridCfg where (<>) s1 s2 = GridCfg {- _grcSizeReqUpdater = _grcSizeReqUpdater s2 <|> _grcSizeReqUpdater s1+ _grcSizeReqUpdater = _grcSizeReqUpdater s1 <> _grcSizeReqUpdater s2 } instance Monoid GridCfg where@@ -60,7 +61,7 @@ instance CmbSizeReqUpdater GridCfg where sizeReqUpdater updater = def {- _grcSizeReqUpdater = Just updater+ _grcSizeReqUpdater = [updater] } -- | Creates a grid of items with the same width.@@ -96,11 +97,11 @@ isVertical = not isHorizontal getSizeReq wenv node children = newSizeReq where- updateSizeReq = fromMaybe id (_grcSizeReqUpdater config)+ sizeReqFns = _grcSizeReqUpdater config vchildren = Seq.filter (_wniVisible . _wnInfo) children newSizeReqW = getDimSizeReq isHorizontal (_wniSizeReqW . _wnInfo) vchildren newSizeReqH = getDimSizeReq isVertical (_wniSizeReqH . _wnInfo) vchildren- newSizeReq = updateSizeReq (newSizeReqW, newSizeReqH)+ newSizeReq = applyFnList sizeReqFns (newSizeReqW, newSizeReqH) getDimSizeReq mainAxis accesor vchildren | Seq.null vreqs = fixedSize 0
src/Monomer/Widgets/Containers/Scroll.hs view
@@ -350,6 +350,7 @@ containerLayoutDirection = layoutDirection, containerGetBaseStyle = getBaseStyle, containerGetCurrentStyle = scrollCurrentStyle,+ containerUpdateCWenv = updateCWenv, containerInit = init, containerMerge = merge, containerFindByPoint = findByPoint,@@ -385,6 +386,28 @@ & L.children . ix 0 . L.info . L.style .~ childStyle | otherwise = node + -- This is overriden to account for space used by scroll bars+ updateCWenv wenv node cnode cidx = newWenv where+ theme = currentTheme wenv node+ barW = fromMaybe (theme ^. L.scrollBarWidth) (_scBarWidth config)+ overlay = fromMaybe (theme ^. L.scrollOverlay) (_scScrollOverlay config)++ ScrollContext{..} = scrollStatus config wenv node state (Point 0 0)+ style = currentStyle wenv node+ carea = getContentArea node style++ -- barH consumes vertical space, barV consumes horizontal space+ barH+ | hScrollRequired && not overlay = barW+ | otherwise = 0+ barV+ | vScrollRequired && not overlay = barW+ | otherwise = 0+ clientArea = subtractFromRect carea 0 barV 0 barH++ newWenv = wenv+ & L.viewport .~ moveRect (negPoint offset) (fromMaybe carea clientArea)+ init wenv node = resultNode newNode where newNode = checkFwdStyle wenv node @@ -393,14 +416,15 @@ & L.widget .~ makeScroll config oldState findByPoint wenv node start point = result where- sctx = scrollStatus config wenv node state point+ -- The point argument already has offset applied+ scrollPoint = subPoint point offset+ sctx = scrollStatus config wenv node state scrollPoint mouseInScroll = (hMouseInScroll sctx && hScrollRequired sctx) || (vMouseInScroll sctx && vScrollRequired sctx)- childPoint = addPoint point offset child = Seq.index (node ^. L.children) 0- childHovered = isPointInNodeVp child childPoint+ childHovered = isPointInNodeVp child point childDragged = isNodePressed wenv child result | (not mouseInScroll && childHovered) || childDragged = Just 0@@ -425,33 +449,34 @@ | otherwise = Nothing ButtonAction point btn status _ -> result where- leftPressed = status == BtnPressed && btn == wenv ^. L.mainButton- btnReleased = status == BtnReleased && btn == wenv ^. L.mainButton+ mainPressed = status == BtnPressed && btn == wenv ^. L.mainButton+ mainReleased = status == BtnReleased && btn == wenv ^. L.mainButton isDragging = isJust $ _sstDragging state- startDrag = leftPressed && not isDragging - jumpScrollH = btnReleased && not isDragging && hMouseInScroll sctx- jumpScrollV = btnReleased && not isDragging && vMouseInScroll sctx+ startDragH = mainPressed && not isDragging && hMouseInThumb sctx+ startDragV = mainPressed && not isDragging && vMouseInThumb sctx - mouseInScroll = hMouseInScroll sctx || vMouseInScroll sctx+ jumpScrollH = mainPressed && not isDragging && hMouseInScroll sctx+ jumpScrollV = mainPressed && not isDragging && vMouseInScroll sctx+ mouseInThumb = hMouseInThumb sctx || vMouseInThumb sctx+ mouseInScroll = hMouseInScroll sctx || vMouseInScroll sctx newState- | startDrag && hMouseInThumb sctx = state { _sstDragging = Just HBar }- | startDrag && vMouseInThumb sctx = state { _sstDragging = Just VBar }+ | startDragH = state { _sstDragging = Just HBar }+ | startDragV = state { _sstDragging = Just VBar } | jumpScrollH = updateScrollThumb state HBar point contentArea sctx | jumpScrollV = updateScrollThumb state VBar point contentArea sctx- | btnReleased = state { _sstDragging = Nothing }+ | mainReleased = state { _sstDragging = Nothing } | otherwise = state newRes = rebuildWidget wenv node newState handledResult = Just $ newRes & L.requests <>~ Seq.fromList scrollReqs result- | leftPressed && mouseInThumb = handledResult- | btnReleased && mouseInScroll = handledResult- | btnReleased && isDragging = handledResult+ | mainPressed && (mouseInThumb || mouseInScroll) = handledResult+ | mainReleased && isDragging = handledResult | otherwise = Nothing Move point | isJust dragging -> result where
src/Monomer/Widgets/Containers/Stack.hs view
@@ -35,6 +35,7 @@ import qualified Data.Sequence as Seq +import Monomer.Helper (applyFnList) import Monomer.Widgets.Container import qualified Monomer.Lens as L@@ -49,19 +50,19 @@ -} data StackCfg = StackCfg { _stcIgnoreEmptyArea :: Maybe Bool,- _stcSizeReqUpdater :: Maybe SizeReqUpdater+ _stcSizeReqUpdater :: [SizeReqUpdater] } instance Default StackCfg where def = StackCfg { _stcIgnoreEmptyArea = Nothing,- _stcSizeReqUpdater = Nothing+ _stcSizeReqUpdater = [] } instance Semigroup StackCfg where (<>) s1 s2 = StackCfg { _stcIgnoreEmptyArea = _stcIgnoreEmptyArea s2 <|> _stcIgnoreEmptyArea s1,- _stcSizeReqUpdater = _stcSizeReqUpdater s2 <|> _stcSizeReqUpdater s1+ _stcSizeReqUpdater = _stcSizeReqUpdater s1 <> _stcSizeReqUpdater s2 } instance Monoid StackCfg where@@ -74,7 +75,7 @@ instance CmbSizeReqUpdater StackCfg where sizeReqUpdater updater = def {- _stcSizeReqUpdater = Just updater+ _stcSizeReqUpdater = [updater] } -- | Creates a horizontal stack.@@ -121,11 +122,11 @@ ignoreEmptyArea = fromMaybe False (_stcIgnoreEmptyArea config) getSizeReq wenv node children = newSizeReq where- updateSizeReq = fromMaybe id (_stcSizeReqUpdater config)+ sizeReqFns = _stcSizeReqUpdater config vchildren = Seq.filter (_wniVisible . _wnInfo) children newSizeReqW = getDimSizeReq isHorizontal (_wniSizeReqW . _wnInfo) vchildren newSizeReqH = getDimSizeReq isVertical (_wniSizeReqH . _wnInfo) vchildren- newSizeReq = updateSizeReq (newSizeReqW, newSizeReqH)+ newSizeReq = applyFnList sizeReqFns (newSizeReqW, newSizeReqH) getDimSizeReq mainAxis accesor vchildren | Seq.null vreqs = fixedSize 0
src/Monomer/Widgets/Single.hs view
@@ -236,11 +236,14 @@ -- | True if border and padding should be added to size requirement. Defaults -- to True. singleAddStyleReq :: Bool,+ -- | If True, the widget will render its background and border. Defaults to+ -- True.+ singleDrawDecorations :: Bool, -- | True if focus should be requested when mouse button is pressed (before -- click). Defaults to True. singleFocusOnBtnPressed :: Bool,- -- | True if style cursor should be ignored. If it's False, cursor changes need- -- to be handled in custom code. Defaults to False.+ -- | True if style cursor should be ignored. If it's False, cursor changes+ -- need to be handled in custom code. Defaults to False. singleUseCustomCursor :: Bool, -- | If true, it will ignore extra space assigned by the parent container, but -- it will not use more space than assigned. Defaults to False.@@ -277,6 +280,7 @@ def = Single { singleAddStyleReq = True, singleFocusOnBtnPressed = True,+ singleDrawDecorations = True, singleUseCustomCursor = False, singleUseCustomSize = False, singleUseScissor = False,@@ -597,10 +601,11 @@ -> IO () renderWrapper single wenv node renderer = drawInScissor renderer useScissor viewport $- drawStyledAction renderer viewport style $ \_ ->+ drawStyledAction_ renderer drawDecorations viewport style $ \_ -> handler wenv node renderer where handler = singleRender single+ drawDecorations = singleDrawDecorations single useScissor = singleUseScissor single style = singleGetCurrentStyle single wenv node viewport = node ^. L.info . L.viewport
src/Monomer/Widgets/Singles/Button.hs view
@@ -50,15 +50,15 @@ - 'ellipsis': if ellipsis should be used for overflown text. - 'multiline': if text may be split in multiple lines. - 'maxLines': maximum number of text lines to show.+- 'resizeFactor': flexibility to have more or less spaced assigned.+- 'resizeFactorW': flexibility to have more or less horizontal spaced assigned.+- 'resizeFactorH': flexibility to have more or less vertical spaced assigned. - 'onFocus': event to raise when focus is received. - 'onFocusReq': 'WidgetRequest' to generate when focus is received. - 'onBlur': event to raise when focus is lost. - 'onBlurReq': 'WidgetRequest' to generate when focus is lost. - 'onClick': event to raise when button is clicked. - 'onClickReq': 'WidgetRequest' to generate when button is clicked.-- 'resizeFactor': flexibility to have more or less spaced assigned.-- 'resizeFactorW': flexibility to have more or less horizontal spaced assigned.-- 'resizeFactorH': flexibility to have more or less vertical spaced assigned. -} data ButtonCfg s e = ButtonCfg { _btnButtonType :: Maybe ButtonType,@@ -190,13 +190,12 @@ makeButton !caption !config = widget where widget = createContainer () def { containerAddStyleReq = False,+ containerDrawDecorations = False, containerUseScissor = True, containerGetBaseStyle = getBaseStyle,- containerGetCurrentStyle = getCurrentStyle, containerInit = init, containerMerge = merge, containerHandleEvent = handleEvent,- containerGetSizeReq = getSizeReq, containerResize = resize } @@ -210,14 +209,6 @@ where ignoreTheme = _btnIgnoreTheme config == Just True - getCurrentStyle wenv node = styleState where- style = node ^. L.info . L.style- isEnabled = node ^. L.info . L.enabled- isActive = isNodeTreeActive wenv node- styleState- | isEnabled && isActive = fromMaybe def (_styleActive style)- | otherwise = currentStyle wenv node- createChildNode wenv node = newNode where nodeStyle = node ^. L.info . L.style labelCfg = _btnLabelCfg config@@ -256,13 +247,6 @@ reqs = _btnOnClickReq config result = resultReqs node reqs resultFocus = resultReqs node [SetFocus (node ^. L.info . L.widgetId)]-- getSizeReq :: ContainerGetSizeReqHandler s e- getSizeReq wenv node children = (newReqW, newReqH) where- -- Main section reqs- child = Seq.index children 0- newReqW = child ^. L.info . L.sizeReqW- newReqH = child ^. L.info . L.sizeReqH resize wenv node viewport children = resized where assignedAreas = Seq.fromList [viewport]
src/Monomer/Widgets/Singles/ExternalLink.hs view
@@ -24,7 +24,6 @@ ) where import Control.Applicative ((<|>))-import Control.Exception (SomeException, catch) import Control.Lens ((&), (^.), (.~)) import Data.Default import Data.Maybe@@ -34,6 +33,7 @@ import qualified Data.Sequence as Seq import qualified Data.Text as T +import Monomer.Helper (catchAny) import Monomer.Widgets.Container import Monomer.Widgets.Singles.Label @@ -46,15 +46,15 @@ - 'ellipsis': if ellipsis should be used for overflown text. - 'multiline': if text may be split in multiple lines. - 'maxLines': maximum number of text lines to show.+- 'resizeFactor': flexibility to have more or less spaced assigned.+- 'resizeFactorW': flexibility to have more or less horizontal spaced assigned.+- 'resizeFactorH': flexibility to have more or less vertical spaced assigned. - 'onFocus': event to raise when focus is received. - 'onFocusReq': 'WidgetRequest' to generate when focus is received. - 'onBlur': event to raise when focus is lost. - 'onBlurReq': 'WidgetRequest' to generate when focus is lost. - 'onClick': event to raise when button is clicked. - 'onClickReq': 'WidgetRequest' to generate when button is clicked.-- 'resizeFactor': flexibility to have more or less spaced assigned.-- 'resizeFactorW': flexibility to have more or less horizontal spaced assigned.-- 'resizeFactorH': flexibility to have more or less vertical spaced assigned. -} data ExternalLinkCfg s e = ExternalLinkCfg { _elcLabelCfg :: LabelCfg s e,@@ -150,12 +150,12 @@ makeExternalLink !caption !url !config = widget where widget = createContainer () def { containerAddStyleReq = False,+ containerDrawDecorations = False, containerUseScissor = True, containerGetBaseStyle = getBaseStyle, containerInit = init, containerMerge = merge, containerHandleEvent = handleEvent,- containerGetSizeReq = getSizeReq, containerResize = resize } @@ -209,13 +209,6 @@ result = resultReqs node requests resultFocus = resultReqs node [SetFocus (node ^. L.info . L.widgetId)] - getSizeReq :: ContainerGetSizeReqHandler s e- getSizeReq wenv node children = (newReqW, newReqH) where- -- Main section reqs- child = Seq.index children 0- newReqW = child ^. L.info . L.sizeReqW- newReqH = child ^. L.info . L.sizeReqH- resize wenv node viewport children = resized where assignedAreas = Seq.fromList [viewport] resized = (resultNode node, assignedAreas)@@ -232,6 +225,3 @@ catchIgnore :: IO () -> IO () catchIgnore task = catchAny task (const $ return ())--catchAny :: IO a -> (SomeException -> IO a) -> IO a-catchAny = catch
src/Monomer/Widgets/Singles/Image.hs view
@@ -51,13 +51,14 @@ import GHC.Generics import Network.HTTP.Client (HttpException(..), HttpExceptionContent(..)) import Network.Wreq+import Network.Wreq.Session (Session) import qualified Codec.Picture as Pic import qualified Data.ByteString as BS import qualified Data.ByteString.Lazy as BSL import qualified Data.Map as Map import qualified Data.Text as T-import qualified Network.Wreq as Wreq+import qualified Network.Wreq.Session as Sess import Monomer.Widgets.Single @@ -438,7 +439,7 @@ -- Get the image's MVar. One MVar per image name/path is created, to allow -- loading images in parallel. The main MVar is only taken until the image's -- MVar is retrieved/created.- sharedMap <- takeMVar sharedMapMVar+ (sharedMap, sess) <- takeMVar sharedMapMVar >>= getImagesSession sharedImgMVar <- case useShared (Map.lookup key sharedMap) of Just mvar -> return mvar Nothing -> newMVar emptyImgState@@ -450,7 +451,7 @@ Just (oldState, oldCount) -> do return (ImageLoaded oldState, Just (oldState, oldCount + 1)) Nothing -> do- res <- loadImage path+ res <- loadImage sess path case res >>= decodeImage of Left loadError -> return (ImageFailed loadError, Nothing)@@ -486,10 +487,10 @@ imgKey :: Text -> Text imgKey path = "image-widget-key-" <> path -loadImage :: Text -> IO (Either ImageLoadError ByteString)-loadImage path+loadImage :: Session -> Text -> IO (Either ImageLoadError ByteString)+loadImage sess path | not (isUrl path) = loadLocal path- | otherwise = loadRemote path+ | otherwise = loadRemote sess path decodeImage :: ByteString -> Either ImageLoadError DynamicImage decodeImage bs = either (Left . ImageInvalid) Right (Pic.decodeImage bs)@@ -503,8 +504,8 @@ then return . Left . ImageLoadFailed $ "Failed to load: " ++ path else return . Right $ content -loadRemote :: Text -> IO (Either ImageLoadError ByteString)-loadRemote name = do+loadRemote :: Session -> Text -> IO (Either ImageLoadError ByteString)+loadRemote sess name = do let path = T.unpack name eresp <- try $ getUrl path @@ -513,7 +514,7 @@ Right r -> Right $ respBody r where respBody r = BSL.toStrict $ r ^. responseBody- getUrl = getWith (defaults & checkResponse ?~ (\_ _ -> return ()))+ getUrl = Sess.getWith (defaults & checkResponse ?~ (\_ _ -> return ())) sess remoteException :: String -> HttpException -> Either ImageLoadError ByteString@@ -544,6 +545,17 @@ isImageSource = ImagePath name, isImageData = Just (bs, size) }++getImagesSession+ :: Map Text WidgetShared+ -> IO (Map Text WidgetShared, Sess.Session)+getImagesSession sharedMap = case useShared (Map.lookup key sharedMap) of+ Just sess -> return (sharedMap, sess)+ Nothing -> do+ sess <- Sess.newAPISession+ return (sharedMap & at key ?~ WidgetShared sess, sess)+ where+ key = "image-widget-wreq-session" isUrl :: Text -> Bool isUrl url = T.isPrefixOf "http://" lurl || T.isPrefixOf "https://" lurl where
src/Monomer/Widgets/Singles/Label.hs view
@@ -133,8 +133,8 @@ labelCurrentStyle :: (WidgetEnv s e -> WidgetNode s e -> StyleState) -> LabelCfg s e-labelCurrentStyle style = def {- _lscCurrentStyle = Just style+labelCurrentStyle styleFn = def {+ _lscCurrentStyle = Just styleFn } data LabelState = LabelState {
src/Monomer/Widgets/Util/Drawing.hs view
@@ -28,6 +28,7 @@ drawArrowDown, drawTimesX, drawStyledAction,+ drawStyledAction_, drawRoundedRect, drawRectRoundedBorder ) where@@ -35,7 +36,6 @@ import Control.Applicative ((<|>)) import Control.Lens ((&), (^.), (^?), (^?!), (.~), non) import Control.Monad (forM_, void, when)-import Data.ByteString (ByteString) import Data.Default import Data.Maybe import Data.Text (Text)@@ -341,12 +341,27 @@ -> StyleState -- ^ The style defining background and border. -> (Rect -> IO ()) -- ^ The drawing actions. They receive the content area. -> IO () -- ^ The resulting action.-drawStyledAction renderer rect style action = do- drawRect renderer rect _sstBgColor _sstRadius+drawStyledAction renderer rect style action =+ drawStyledAction_ renderer True rect style action +{-|+Runs a set of rendering operations after conditionally drawing the style's+background, and before drawing the style's border.+-}+drawStyledAction_+ :: Renderer -- ^ The renderer.+ -> Bool -- ^ Whether background and border should be drawn.+ -> Rect -- ^ The rect where background and border will be drawn.+ -> StyleState -- ^ The style defining background and border.+ -> (Rect -> IO ()) -- ^ The drawing actions. They receive the content area.+ -> IO () -- ^ The resulting action.+drawStyledAction_ renderer drawDecorations rect style action = do+ when drawDecorations $+ drawRect renderer rect _sstBgColor _sstRadius+ forM_ contentRect action - when (isJust _sstBorder) $+ when (drawDecorations && isJust _sstBorder) $ drawRectBorder renderer rect (fromJust _sstBorder) _sstRadius where StyleState{..} = style
src/Monomer/Widgets/Util/Widget.hs view
@@ -40,7 +40,6 @@ import Control.Concurrent (threadDelay) import Control.Lens ((&), (^#), (#~), (^.), (^?), (.~), (%~), _Just)-import Data.ByteString.Lazy (ByteString) import Data.Default import Data.Maybe import Data.Map.Strict (Map)
+ test/unit/Monomer/Core/SizeReqSpec.hs view
@@ -0,0 +1,30 @@+{-|+Module : Monomer.Core.SizeReqSpec+Copyright : (c) 2018 Francisco Vallarino+License : BSD-3-Clause (see the LICENSE file)+Maintainer : fjvallarino@gmail.com+Stability : experimental+Portability : non-portable++Unit tests for SizeReq functions.+-}+module Monomer.Core.SizeReqSpec (spec) where++import Test.Hspec++import Monomer.Core.Combinators+import Monomer.Core.SizeReq++spec :: Spec+spec = describe "SizeReq" $ do+ sizeReqUpdaterSpec++sizeReqUpdaterSpec :: Spec+sizeReqUpdaterSpec = describe "SizeReqUpdater" $ do+ it "should update fixed size to minimum size" $ do+ fixedToMinW 1 (width 100, height 100) `shouldBe` (minWidth 100, height 100)+ fixedToMinH 1 (width 100, height 100) `shouldBe` (width 100, minHeight 100)++ it "should update fixed size to expand size" $ do+ fixedToExpandW 1 (width 100, height 100) `shouldBe` (expandWidth 100, height 100)+ fixedToExpandH 1 (width 100, height 100) `shouldBe` (width 100, expandHeight 100)
test/unit/Monomer/Graphics/UtilSpec.hs view
@@ -1,5 +1,5 @@ {-|-Module : Monomer.Common.CursorIconSpec+Module : Monomer.Graphics.UtilSpec Copyright : (c) 2018 Francisco Vallarino License : BSD-3-Clause (see the LICENSE file) Maintainer : fjvallarino@gmail.com
test/unit/Monomer/TestUtil.hs view
@@ -159,6 +159,7 @@ mockWenv :: s -> WidgetEnv s e mockWenv model = WidgetEnv { _weOs = "Mac OS X",+ _weDpr = 2, _weFontManager = mockFontManager, _weFindByPath = const Nothing, _weMainButton = BtnLeft,
test/unit/Monomer/Widgets/Containers/BoxSpec.hs view
@@ -170,8 +170,7 @@ where wenv = mockWenvEvtUnit ()- updater (rw, rh) = (minSize (rw ^. L.fixed) 2, maxSize (rh ^. L.fixed) 3)- boxNode = box_ [sizeReqUpdater updater] (label "Label")+ boxNode = box_ [sizeReqUpdater (fixedToMinW 2), sizeReqUpdater (fixedToMaxH 3)] (label "Label") (sizeReqW, sizeReqH) = nodeGetSizeReq wenv boxNode resize :: Spec
test/unit/Monomer/Widgets/Containers/GridSpec.hs view
@@ -133,8 +133,7 @@ where wenv = mockWenv ()- updater (rw, rh) = (minSize (rw ^. L.fixed) 2, maxSize (rh ^. L.fixed) 3)- vgridNode = vgrid_ [sizeReqUpdater updater] [label "Label"]+ vgridNode = vgrid_ [sizeReqUpdater (fixedToMinW 2), sizeReqUpdater (fixedToMaxH 3)] [label "Label"] (sizeReqW, sizeReqH) = nodeGetSizeReq wenv vgridNode resize :: Spec
test/unit/Monomer/Widgets/Containers/ScrollSpec.hs view
@@ -19,6 +19,7 @@ import Monomer.Core import Monomer.Core.Combinators+import Monomer.Core.Themes.SampleThemes import Monomer.Event import Monomer.Graphics.ColorTable import Monomer.TestEventUtil@@ -47,9 +48,48 @@ handleEvent :: Spec handleEvent = describe "handleEvent" $ do+ handleBarClick handleChildrenFocus handleNestedWheel handleMessageReset++handleBarClick :: Spec+handleBarClick = describe "handleBarClick" $ do+ it "should click the first button" $ do+ evts [evtClick point] `shouldBe` Seq.fromList [Button1]++ it "should scroll right and click the third button" $ do+ evts [evtPress midHBar, evtClick point] `shouldBe` Seq.fromList [Button2]++ it "should scroll down and click the third button" $ do+ evts [evtPress midVBar, evtClick point] `shouldBe` Seq.fromList [Button3]++ it "should scroll down and click the third button" $ do+ evts [evtPress midVBar, evtClick point] `shouldBe` Seq.fromList [Button3]++ it "should scroll down and right and click the fourth button" $ do+ evts [evtPress midHBar, evtPress midVBar, evtClick point] `shouldBe` Seq.fromList [Button4]++ where+ wenv = mockWenv ()+ & L.theme .~ darkTheme+ & L.windowSize .~ Size 640 480+ point = Point 320 200+ midHBar = Point 630 476+ midVBar = Point 636 470+ st = [width 640, height 480]+ stackNode = vstack [+ hstack [+ button "Button 1" Button1 `styleBasic` st,+ button "Button 2" Button2 `styleBasic` st+ ],+ hstack [+ button "Button 3" Button3 `styleBasic` st,+ button "Button 4" Button4 `styleBasic` st+ ]+ ]+ scrollNode = scroll stackNode+ evts es = nodeHandleEventEvts wenv es scrollNode handleChildrenFocus :: Spec handleChildrenFocus = describe "handleChildrenFocus" $ do
test/unit/Monomer/Widgets/Containers/StackSpec.hs view
@@ -73,16 +73,24 @@ getSizeReqUpdater :: Spec getSizeReqUpdater = describe "getSizeReqUpdater" $ do it "should return width = Min 50 2" $- sizeReqW `shouldBe` minSize 50 2+ sizeReqW1 `shouldBe` minSize 50 2 it "should return height = Max 20" $- sizeReqH `shouldBe` maxSize 20 3+ sizeReqH1 `shouldBe` maxSize 20 3 + it "should return width = Min 50 10" $+ sizeReqW2 `shouldBe` minSize 50 10++ it "should return height = Max 20 15" $+ sizeReqH2 `shouldBe` maxSize 20 15+ where wenv = mockWenv () updater (rw, rh) = (minSize (rw ^. L.fixed) 2, maxSize (rh ^. L.fixed) 3)- vstackNode = vstack_ [sizeReqUpdater updater] [label "Label"]- (sizeReqW, sizeReqH) = nodeGetSizeReq wenv vstackNode+ vstackNode1 = vstack_ [sizeReqUpdater updater] [label "Label"]+ vstackNode2 = vstack_ [sizeReqUpdater (fixedToMinW 10), sizeReqUpdater (fixedToMaxH 15)] [label "Label"]+ (sizeReqW1, sizeReqH1) = nodeGetSizeReq wenv vstackNode1+ (sizeReqW2, sizeReqH2) = nodeGetSizeReq wenv vstackNode2 resize :: Spec resize = describe "resize" $ do
test/unit/Spec.hs view
@@ -6,6 +6,7 @@ import qualified SDL.Raw as Raw import qualified Monomer.Common.CursorIconSpec as CursorIconSpec+import qualified Monomer.Core.SizeReqSpec as SizeReqSpec import qualified Monomer.Graphics.UtilSpec as GraphicsUtilSpec import qualified Monomer.Widgets.CompositeSpec as CompositeSpec@@ -65,6 +66,7 @@ spec :: Spec spec = do common+ core graphics widgets widgetsUtil@@ -72,6 +74,10 @@ common :: Spec common = describe "Common" $ do CursorIconSpec.spec++core :: Spec+core = describe "Core" $ do+ SizeReqSpec.spec graphics :: Spec graphics = describe "Graphics" $ do