16 Commits

Author SHA1 Message Date
a95d95bb65 Prepare 1.3.0 (#120) 2021-12-22 17:40:24 +03:00
23efa7cb02 Fill in changelog for 1.2.1 (#119) 2021-12-22 11:56:55 +00:00
bb94341ad5 Extended font & glyph support (#114)
* Separate font utils, add glyph support (#113)
* Implement font glyph ranges builder
* Implement raw font config binds
* Implement font atlas module
* Rewrite font altas rebuilder in Managed
2021-12-22 13:28:46 +03:00
13e68242a1 Update unordered-containers upper limit (#117) 2021-12-19 18:25:11 +03:00
2469623f2e Fix CI (#118) 2021-12-19 17:32:28 +03:00
3087a99044 Allow megaparsec 9.2 (#112)
Closes #106
2021-10-30 18:57:15 +00:00
f74cd218c5 Bump imgui to 1.85 (#111)
Closes #110
2021-10-30 18:49:39 +00:00
48c8ae0379 Add IO exports (#109) 2021-10-04 18:49:16 +00:00
a2c0c0658e Remove library options preventing hackage upload (#108) 2021-10-04 18:41:07 +00:00
21ce5cabd8 Add ImGuiIO basics (#107) 2021-10-04 17:29:32 +00:00
8db9ddec2f Added compiler flags stanzas. (#105) 2021-09-21 15:14:37 +03:00
4ecf62ac9e Implement ImGuiListClipper (#100)
* Added DearImGui.Raw.ImGuiListClipper
* Added the DearImGui.withListClipper bracket
* Added dependency for `vector`
* Added the test in the Main.hs
2021-09-17 11:09:22 +03:00
8ee82476dc Add raw DrawList bindings (#99) 2021-09-15 08:52:00 +03:00
b4bc36ca89 Update readme example (#103)
Fixes #68
2021-09-14 15:41:38 +03:00
76ce7bb569 Nix upgrades (#102)
Switches the default Nix build to use GHC 8.10.7 and NixOS 21.05
2021-09-14 01:58:39 +00:00
259ffbff48 Fix DearImGui TOC (#101) 2021-09-13 17:05:49 +00:00
23 changed files with 2737 additions and 126 deletions

View File

@ -9,13 +9,14 @@ jobs:
persist-credentials: false
submodules: true
- uses: cachix/install-nix-action@v12
- uses: cachix/install-nix-action@v16
with:
nix_path: nixpkgs=channel:nixos-unstable
- uses: cachix/cachix-action@v8
- uses: cachix/cachix-action@v10
with:
name: hs-dear-imgui
authToken: '${{ secrets.CACHIX_AUTH_TOKEN }}'
- run: nix-build --version
- run: nix-build -A hsPkgs.dear-imgui.components.exes

View File

@ -1,5 +1,20 @@
# Changelog for dear-imgui
## [1.3.0]
- Added `DearImGui.FontAtlas` and related `DearImGui.Raw.Font.*` bits.
- Removed old font atlas functions from `DearImGui` and `DearImGui.Raw`.
## [1.2.2]
- `imgui` updated to 1.85.
## [1.2.1]
- Added `DearImGui.Raw.DrawList` for drawing primitives.
- Added `DearImGui.Raw.IO` with attribute setters.
- Added `DearImGui.Raw.ListClipper` for efficient list viewports.
## [1.2.0]
- Fixed `nullPtr` in place of default arguments.
@ -36,3 +51,6 @@ Initial Hackage release based on 1.83.
[1.0.2]: https://github.com/haskell-game/dear-imgui.hs/tree/v1.0.2
[1.1.0]: https://github.com/haskell-game/dear-imgui.hs/tree/v1.1.0
[1.2.0]: https://github.com/haskell-game/dear-imgui.hs/tree/v1.2.0
[1.2.1]: https://github.com/haskell-game/dear-imgui.hs/tree/v1.2.1
[1.2.2]: https://github.com/haskell-game/dear-imgui.hs/tree/v1.2.2
[1.3.0]: https://github.com/haskell-game/dear-imgui.hs/tree/v1.3.0

16
Main.hs
View File

@ -7,6 +7,7 @@ module Main (main) where
import Control.Monad
import Data.IORef
import qualified Data.Vector as Vector
import DearImGui
import DearImGui.OpenGL3
import DearImGui.SDL
@ -131,6 +132,21 @@ loop window checked color slider r pos size' selected tab1Ref tab2Ref = do
endChild
text "ListClipper"
withChildOpen "##fixed" (ImVec2 0 200) True ImGuiWindowFlags_None do
let lotsOfItems = Vector.generate 50 (mappend "Item " . show)
withListClipper Nothing lotsOfItems text
text "ListClipper, Haskell-powered"
withChildOpen "##infinite" (ImVec2 0 200) True ImGuiWindowFlags_None do
let infiniteItems = map (mappend "Item " . show) [0 :: Int ..]
withListClipper Nothing infiniteItems text
text "Ethereal ListClipper"
withChildOpen "##ethereal" (ImVec2 0 200) True ImGuiWindowFlags_None do
withListClipper Nothing (ClipRange (0 :: Int) 1000) $
text . mappend "Item " . show
plotHistogram "A histogram" [ 10, 10, 20, 30, 90 ]
colorPicker3 "Test" color

View File

@ -54,38 +54,35 @@ main = do
runManaged do
-- Create a window using SDL. As we're using OpenGL, we need to enable OpenGL too.
w <- do
window <- do
let title = "Hello, Dear ImGui!"
let config = defaultWindow { windowGraphicsContext = OpenGLContext defaultOpenGL }
managed $ bracket (createWindow title config) destroyWindow
-- Create an OpenGL context
glContext <- managed $ bracket (glCreateContext w) glDeleteContext
glContext <- managed $ bracket (glCreateContext window) glDeleteContext
-- Create an ImGui context
_ <- managed $ bracket createContext destroyContext
-- Initialize ImGui's SDL2 backend
_ <- managed_ $ bracket_ (sdl2InitForOpenGL w glContext) sdl2Shutdown
_ <- managed_ $ bracket_ (sdl2InitForOpenGL window glContext) sdl2Shutdown
-- Initialize ImGui's OpenGL backend
_ <- managed_ $ bracket_ openGL2Init openGL2Shutdown
liftIO $ mainLoop w
liftIO $ mainLoop window
mainLoop :: Window -> IO ()
mainLoop w = do
-- Process the event loop
untilNothingM pollEventWithImGui
mainLoop window = unlessQuit do
-- Tell ImGui we're starting a new frame
openGL2NewFrame
sdl2NewFrame
newFrame
-- Build the GUI
bracket_ (begin "Hello, ImGui!") end do
withWindowOpen "Hello, ImGui!" do
-- Add a text widget
text "Hello, ImGui!"
@ -103,12 +100,25 @@ mainLoop w = do
render
openGL2RenderDrawData =<< getDrawData
glSwapWindow w
glSwapWindow window
mainLoop w
mainLoop window
where
untilNothingM m = m >>= maybe (return ()) (\_ -> untilNothingM m)
-- Process the event loop
unlessQuit action = do
shouldQuit <- checkEvents
if shouldQuit then pure () else action
checkEvents = do
pollEventWithImGui >>= \case
Nothing ->
return False
Just event ->
(isQuit event ||) <$> checkEvents
isQuit event =
SDL.eventPayload event == SDL.QuitEvent
```
# Hacking

View File

@ -1,7 +1,7 @@
cabal-version: 3.0
name: dear-imgui
version: 1.2.0
version: 1.3.0
author: Oliver Charles
maintainer: ollie@ocharles.org.uk, aenor.realm@gmail.com
license: BSD-3-Clause
@ -24,10 +24,45 @@ extra-source-files:
imgui/imconfig.h,
imgui/LICENSE.txt
common build-flags
if flag(debug)
if os(linux)
ghc-options: -Wall -g -rtsopts -dcore-lint -debug
cc-options: -g -O0 -fsanitize=address -fno-omit-frame-pointer
cxx-options: -g -O0 -fsanitize=address -fno-omit-frame-pointer -std=c++11
if os(darwin)
ghc-options: -Wall -g -rtsopts -dcore-lint -debug
cc-options: -g -O0 -fsanitize=address -fno-omit-frame-pointer
cxx-options: -g -O0 -fsanitize=address -fno-omit-frame-pointer -std=c++11
if os(windows)
ghc-options: -Wall -g -rtsopts -dcore-lint -debug
cc-options: -g -O0
cxx-options: -g -O0 -std=c++11
else
if os(linux)
ghc-options: -Wall -O2
cc-options: -O2
cxx-options: -std=c++11 -O2
if os(darwin)
ghc-options: -Wall -O2
cc-options: -O2
if os(windows)
ghc-options: -Wall -O2
cc-options: -O2
source-repository head
type: git
location: https://github.com/haskell-game/dear-imgui.hs
flag debug
description:
Enable debug mode.
default:
False
manual:
True
flag opengl2
description:
Enable OpenGL 2 backend.
@ -82,8 +117,6 @@ common common
>= 4.12 && < 4.17
default-language:
Haskell2010
ghc-options:
-Wall
library
import: common
@ -91,19 +124,25 @@ library
src
exposed-modules:
DearImGui
DearImGui.FontAtlas
DearImGui.Raw
DearImGui.Raw.DrawList
DearImGui.Raw.Font
DearImGui.Raw.Font.Config
DearImGui.Raw.Font.GlyphRanges
DearImGui.Raw.ListClipper
DearImGui.Raw.IO
other-modules:
DearImGui.Context
DearImGui.Enums
DearImGui.Structs
cxx-options: -std=c++11
cxx-sources:
imgui/imgui.cpp
imgui/imgui_demo.cpp
imgui/imgui_draw.cpp
imgui/imgui_tables.cpp
imgui/imgui_widgets.cpp
cxx-options:
-std=c++11
extra-libraries:
stdc++
include-dirs:
@ -116,6 +155,7 @@ library
, inline-c-cpp
, StateVar
, unliftio
, vector
if flag(opengl2)
exposed-modules:
@ -218,7 +258,7 @@ library dear-imgui-generator
, inline-c
>= 0.9.0.0 && < 0.10
, megaparsec
>= 9.0 && < 9.1
>= 9.0 && < 9.3
, parser-combinators
>= 1.2.0 && < 1.4
, scientific
@ -230,38 +270,45 @@ library dear-imgui-generator
, transformers
>= 0.5.6 && < 0.6
, unordered-containers
>= 0.2.11 && < 0.2.15
>= 0.2.11 && < 0.3
executable test
import: common
import: common, build-flags
main-is: Main.hs
default-language: Haskell2010
ghc-options: -Wall
if (!flag(examples) || !flag(sdl) || !flag(opengl2))
buildable: False
else
build-depends: base, sdl2, gl, dear-imgui
build-depends: base, sdl2, gl, dear-imgui, vector
executable glfw
import: common, build-flags
main-is: Main.hs
hs-source-dirs: examples/glfw
default-language: Haskell2010
ghc-options: -Wall
if (!flag(examples) || !flag(glfw) || !flag(opengl2))
buildable: False
else
build-depends: base, GLFW-b, gl, dear-imgui, managed
executable readme
import: common
import: common, build-flags
main-is: Readme.hs
hs-source-dirs: examples
build-depends: sdl2, gl, dear-imgui, managed
if (!flag(examples) || !flag(sdl) || !flag(opengl2))
buildable: False
executable fonts
import: common, build-flags
main-is: Main.hs
hs-source-dirs: examples/fonts
build-depends: sdl2, gl, dear-imgui, managed
if (!flag(examples) || !flag(sdl) || !flag(opengl2))
buildable: False
executable image
import: common
import: common, build-flags
main-is: Image.hs
hs-source-dirs: examples/sdl
build-depends: sdl2, gl, dear-imgui, managed, vector
@ -269,12 +316,11 @@ executable image
buildable: False
executable vulkan
import: common
import: common, build-flags
main-is: Main.hs
other-modules: Attachments, Backend, Input, Util
hs-source-dirs: examples/vulkan
default-language: Haskell2010
ghc-options: -Wall
if (!flag(examples) || !flag(sdl) || !flag(vulkan))
buildable: False
else

View File

@ -4,12 +4,12 @@ haskellNix ? (import (import ./nix/sources.nix)."haskell.nix" { })
# haskell.nix provides access to the nixpkgs pins which are used by our CI,
# hence you will be more likely to get cache hits when using these.
# But you can also just use your own, e.g. '<nixpkgs>'.
, nixpkgsSrc ? haskellNix.sources.nixpkgs-2009
, nixpkgsSrc ? haskellNix.sources.nixpkgs-2105
# haskell.nix provides some arguments to be passed to nixpkgs, including some
# patches and also the haskell.nix functionality itself as an overlay.
, nixpkgsArgs ? haskellNix.nixpkgsArgs
, compiler-nix-name ? "ghc884"
, compiler-nix-name ? "ghc8107"
}:
let
pkgs = import nixpkgsSrc nixpkgsArgs;

View File

@ -45,10 +45,7 @@ main = do
mainLoop :: Window -> IO ()
mainLoop window = do
-- Process the event loop
untilNothingM pollEventWithImGui
mainLoop window = unlessQuit do
-- Tell ImGui we're starting a new frame
openGL2NewFrame
sdl2NewFrame
@ -78,4 +75,17 @@ mainLoop window = do
mainLoop window
where
untilNothingM m = m >>= maybe (return ()) (\_ -> untilNothingM m)
-- Process the event loop
unlessQuit action = do
shouldQuit <- checkEvents
if shouldQuit then pure () else action
checkEvents = do
pollEventWithImGui >>= \case
Nothing ->
return False
Just event ->
(isQuit event ||) <$> checkEvents
isQuit event =
SDL.eventPayload event == SDL.QuitEvent

150
examples/fonts/Main.hs Normal file
View File

@ -0,0 +1,150 @@
{-# language BlockArguments #-}
{-# language LambdaCase #-}
{-# language OverloadedStrings #-}
{-# language RecordWildCards #-}
{-# language NamedFieldPuns #-}
{-# language DeriveTraversable #-}
{- | Font usage example.
Loads two non-standard fonts
This example uses NotoSansJP-Regular.otf from Google Fonts
Licensed under the SIL Open Font License, Version 1.1
https://fonts.google.com/noto/specimen/Noto+Sans+JP
-}
module Main ( main ) where
import Control.Exception
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Managed
import Data.IORef
import DearImGui
import qualified DearImGui.FontAtlas as FontAtlas
import DearImGui.OpenGL2
import DearImGui.SDL
import DearImGui.SDL.OpenGL
import Graphics.GL
import SDL
-- Rebuild syntax enables us to keep fonts in any
-- traversable type, so let's make our life a little easier.
-- But feel free to use lists or maps.
data FontSet a = FontSet
{ droidFont :: a
, defaultFont :: a
, notoFont :: a
}
deriving (Functor, Foldable, Traversable)
main :: IO ()
main = do
-- Window initialization is similar to another examples.
initializeAll
runManaged do
window <- do
let title = "Hello, Dear ImGui!"
let config = defaultWindow { windowGraphicsContext = OpenGLContext defaultOpenGL }
managed $ bracket (createWindow title config) destroyWindow
glContext <- managed $ bracket (glCreateContext window) glDeleteContext
_ <- managed $ bracket createContext destroyContext
_ <- managed_ $ bracket_ (sdl2InitForOpenGL window glContext) sdl2Shutdown
_ <- managed_ $ bracket_ openGL2Init openGL2Shutdown
-- We use high-level syntax to build font atlas and
-- get handles to use in the main loop.
fontSet <- FontAtlas.rebuild FontSet
{ -- The first mentioned font is loaded first
-- and set as a global default.
droidFont =
FontAtlas.FromTTF
"./imgui/misc/fonts/DroidSans.ttf"
15
Nothing
FontAtlas.Cyrillic
-- You also may use a default hardcoded font for
-- some purposes (i.e. as fallback)
, defaultFont =
FontAtlas.DefaultFont
-- To optimize atlas size, use ranges builder and
-- provide source localization data.
, notoFont =
FontAtlas.FromTTF
"./examples/fonts/NotoSansJP-Regular.otf"
20
Nothing
( FontAtlas.RangesBuilder $ mconcat
[ FontAtlas.addRanges FontAtlas.Latin
, FontAtlas.addText "私をクリックしてください"
, FontAtlas.addText "こんにちは"
]
)
}
liftIO $ do
fontFlag <- newIORef False
mainLoop window do
let FontSet{..} = fontSet
withWindowOpen "Hello, ImGui!" do
-- To use a font for widget text, you may either put it
-- into a 'withFont' block:
withFont defaultFont do
text "Hello, ImGui!"
text "Привет, ImGui!"
-- ...or you can explicitly push and pop a font.
-- Though it's not recommended.
toggled <- readIORef fontFlag
when toggled $
pushFont notoFont
-- Some of those are only present in Noto font range
-- and will render as `?`s.
text "こんにちは, ImGui!"
let buttonText = if toggled then "私をクリックしてください" else "Click Me!"
button buttonText >>= \clicked ->
when clicked $
modifyIORef' fontFlag not
when toggled
popFont
showDemoWindow
mainLoop :: Window -> IO () -> IO ()
mainLoop window frameAction = loop
where
loop = unlessQuit do
openGL2NewFrame
sdl2NewFrame
newFrame
frameAction
glClear GL_COLOR_BUFFER_BIT
render
openGL2RenderDrawData =<< getDrawData
glSwapWindow window
loop
unlessQuit action = do
shouldQuit <- checkEvents
if shouldQuit then pure () else action
checkEvents = do
pollEventWithImGui >>= \case
Nothing ->
return False
Just event ->
(isQuit event ||) <$> checkEvents
isQuit event =
SDL.eventPayload event == SDL.QuitEvent

Binary file not shown.

View File

@ -14,6 +14,7 @@ import Control.Monad.IO.Class (MonadIO(..))
import Control.Monad.Managed (managed, managed_, runManaged)
import DearImGui
import qualified DearImGui.Raw as Raw
import qualified DearImGui.Raw.DrawList as DrawList
import DearImGui.OpenGL3
import DearImGui.SDL
import DearImGui.SDL.OpenGL
@ -134,18 +135,18 @@ mainLoop window textures flag = unlessQuit do
sdl2NewFrame
newFrame
let texture = if flag then fst textures else snd textures
-- Drawing images require some backend-specific code.
-- Meanwhile, we have to deal with raw bindings.
let openGLtextureID = intPtrToPtr $ fromIntegral $ textureID texture
-- Build the GUI
clicked <- withWindow "Image example" \open ->
if open then do
text "That's an image, click it"
newLine
let texture = if flag then fst textures else snd textures
-- Drawing images require some backend-specific code.
-- Meanwhile, we have to deal with raw binding.
let openGLtextureID = intPtrToPtr $ fromIntegral $ textureID texture
-- Using imageButton
Foreign.with (textureSize texture) \sizePtr ->
Foreign.with (ImVec2 0 0) \uv0Ptr ->
Foreign.with (ImVec2 1 1) \uv1Ptr ->
@ -155,6 +156,19 @@ mainLoop window textures flag = unlessQuit do
else
pure False
-- Using DrawList
bg <- getBackgroundDrawList
Foreign.with (ImVec2 100 100) \pMin ->
Foreign.with (ImVec2 200 200) \pMax ->
Foreign.with (ImVec2 0.25 0.25) \uvMin ->
Foreign.with (ImVec2 0.75 0.75) \uvMax ->
DrawList.addImageRounded
bg
openGLtextureID
pMin pMax uvMin uvMax
(Raw.imCol32 0 255 0 0xFF) -- Extract green channel
32 ImDrawFlags_RoundCornersBottom
-- Render
glClear GL_COLOR_BUFFER_BIT

View File

@ -134,12 +134,6 @@ app = do
ImGui.createContext
ImGui.destroyContext
logDebug "Adding fonts"
ImGui.clearFontAtlas
_default <- ImGui.addFontDefault
_custom <- ImGui.addFontFromFileTTF "imgui/misc/fonts/ProggyTiny.ttf" 10
ImGui.buildFontAtlas
let
preferredFormat :: Vulkan.SurfaceFormatKHR
preferredFormat =

2
imgui

Submodule imgui updated: e3e1fbcf02...55d35d8387

View File

@ -3,6 +3,7 @@
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
@ -54,6 +55,7 @@ module DearImGui
-- ** Utilities
, Raw.getWindowDrawList
, Raw.getWindowPos
, Raw.getWindowSize
, Raw.getWindowWidth
@ -84,6 +86,11 @@ module DearImGui
, pushStyleVar
, popStyleVar
, withFont
, Raw.Font.pushFont
, Raw.Font.popFont
, Raw.Font.Font
-- * Cursor/Layout
, Raw.separator
, Raw.sameLine
@ -172,11 +179,11 @@ module DearImGui
, inputTextMultiline
, inputTextWithHint
-- * Color Editor/Picker
-- ** Color Editor/Picker
, colorPicker3
, colorButton
-- * Trees
-- ** Trees
, treeNode
, treePush
, Raw.treePop
@ -187,7 +194,7 @@ module DearImGui
-- ** List Boxes
, listBox
-- * Data Plotting
-- ** Data Plotting
, plotHistogram
-- ** Menus
@ -221,7 +228,7 @@ module DearImGui
, tabItemButton
, setTabItemClosed
-- * Tooltips
-- ** Tooltips
, withTooltip
, Raw.beginTooltip
, Raw.endTooltip
@ -245,12 +252,17 @@ module DearImGui
, Raw.wantCaptureMouse
, Raw.wantCaptureKeyboard
-- * Fonts
, Raw.Font
, addFontFromFileTTF
, Raw.addFontDefault
, Raw.buildFontAtlas
, Raw.clearFontAtlas
-- * Utilities
-- ** ListClipper
, withListClipper
, ClipItems(..)
, ClipRange(..)
-- ** Miscellaneous
, Raw.getBackgroundDrawList
, Raw.getForegroundDrawList
, Raw.imCol32
-- * Types
, module DearImGui.Enums
@ -273,6 +285,9 @@ import System.IO
-- dear-imgui
import DearImGui.Enums
import DearImGui.Structs
import qualified DearImGui.Raw as Raw
import qualified DearImGui.Raw.Font as Raw.Font
import qualified DearImGui.Raw.ListClipper as Raw.ListClipper
-- managed
import qualified Control.Monad.Managed as Managed
@ -289,8 +304,10 @@ import Control.Monad.IO.Class
import UnliftIO (MonadUnliftIO)
import UnliftIO.Exception (bracket, bracket_)
import qualified DearImGui.Raw as Raw
-- vector
import qualified Data.Vector as V
import qualified Data.Vector.Storable as VS
import qualified Data.Vector.Unboxed as VU
-- | Get the compiled version string e.g. "1.80 WIP" (essentially the value for
-- @IMGUI_VERSION@ from the compiled version of @imgui.cpp@).
@ -1672,21 +1689,89 @@ popStyleVar :: (MonadIO m) => Int -> m ()
popStyleVar n = liftIO do
Raw.popStyleVar (fromIntegral n)
-- | Render widgets inside the block using provided font.
withFont :: MonadUnliftIO m => Raw.Font.Font -> m a -> m a
withFont font = bracket_ (Raw.Font.pushFont font) Raw.Font.popFont
-- | Load a font from TTF file.
-- | Clips a large list of items
--
-- Specify font path and atlas glyph size.
--
-- Use 'addFontDefault' if you want to retain built-in font too.
--
-- Call 'buildFontAtlas' after adding all the fonts.
--
-- Call backend-specific `CreateFontsTexture` before using 'newFrame'.
addFontFromFileTTF :: MonadIO m => FilePath -> Float -> m (Maybe Raw.Font)
addFontFromFileTTF font size = liftIO do
res@(Raw.Font ptr) <- withCString font \fontPtr ->
Raw.addFontFromFileTTF fontPtr (CFloat size)
pure $
if castPtr ptr == nullPtr
then Nothing
else Just res
-- The requirements on @a@ are that they are all of the same height.
withListClipper :: (ClipItems t a, MonadUnliftIO m) => Maybe Float -> t a -> (a -> m ()) -> m ()
withListClipper itemHeight items action =
bracket
(liftIO $ throwIfNull "withListClipper: ListClipper allocation failed" Raw.ListClipper.new)
Raw.ListClipper.delete
step
where
itemHeight' = maybe (-1.0) CFloat itemHeight
itemCount' = maybe maxBound fromIntegral (itemCount items)
step clipper = do
Raw.ListClipper.begin clipper itemCount' itemHeight'
go clipper
go clipper = do
doStep <- Raw.ListClipper.step clipper
when doStep do
let
startIndex = fromIntegral $ Raw.ListClipper.displayStart clipper
endIndex = fromIntegral $ Raw.ListClipper.displayEnd clipper
stepItems action $
clipItems startIndex endIndex items
go clipper
-- | Containers usable with 'ListClipper'.
class ClipItems t a where
itemCount :: t a -> Maybe Int
clipItems :: Int -> Int -> t a -> t a
stepItems :: Monad m => (a -> m ()) -> t a -> m ()
-- | Unbounded stream of items.
instance ClipItems [] a where
itemCount = const Nothing
clipItems displayStart displayEnd =
take (displayEnd - displayStart) . drop displayStart
stepItems = mapM_
instance ClipItems V.Vector a where
itemCount = Just . V.length
clipItems displayStart displayEnd =
V.slice displayStart (displayEnd - displayStart)
stepItems = V.mapM_
instance Storable a => ClipItems VS.Vector a where
itemCount = Just . VS.length
clipItems displayStart displayEnd =
VS.slice displayStart (displayEnd - displayStart)
stepItems = VS.mapM_
instance VU.Unbox a => ClipItems VU.Vector a where
itemCount = Just . VU.length
clipItems displayStart displayEnd =
VU.slice displayStart (displayEnd - displayStart)
stepItems = VU.mapM_
-- | ClipList helper for arbitrary unmaterialized ranges.
data ClipRange a = ClipRange a a
deriving (Eq, Ord, Show)
instance (Ord a, Enum a, Num a) => ClipItems ClipRange a where
itemCount (ClipRange _begin end) =
Just $ fromEnum end
clipItems clipBegin clipEnd (ClipRange oldBegin oldEnd) =
ClipRange
(toEnum $ max clipBegin $ fromEnum oldBegin)
(toEnum $ min clipEnd $ fromEnum oldEnd)
stepItems action (ClipRange start end) =
mapM_ action [start .. end - 1]

View File

@ -33,7 +33,13 @@ imguiContext = mempty
[ ( TypeName "ImVec2", [t| ImVec2 |] )
, ( TypeName "ImVec3", [t| ImVec3 |] )
, ( TypeName "ImVec4", [t| ImVec4 |] )
, ( TypeName "ImU32", [t| ImU32 |] )
, ( TypeName "ImWchar", [t| ImWchar |] )
, ( TypeName "ImDrawList", [t| ImDrawList |] )
, ( TypeName "ImGuiContext", [t| ImGuiContext |] )
, ( TypeName "ImFont", [t| ImFont |] )
, ( TypeName "ImFontConfig", [t| ImFontConfig |] )
, ( TypeName "ImFontGlyphRangesBuilder", [t| ImFontGlyphRangesBuilder |] )
, ( TypeName "ImGuiListClipper", [t| ImGuiListClipper |] )
]
}

501
src/DearImGui/FontAtlas.hs Normal file
View File

@ -0,0 +1,501 @@
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-|
Module: DearImGui.FontAtlas
Font atlas builder, accompanied with lower-level functions.
@
import qualified DearImGui.FontAtlas as FontAtlas
prepareAtlas =
FontAtlas.rebuild
[ FontAtlas.FileTTF "comic-sans-mono.ttf" 13 csOptions csRanges
, FontAtlas.Default
]
where
csOptions = mconcat
[ FontAtlas.fontNo 1
, FontAtlas.glyphOffset (0, -1)
]
csRanges = RangeBuilder $ mconcat
[ FontAtlas.addText "Hello world"
, FontRanges.addChar 'Ꙑ'
, FontRanges.addRanges FontRanges.Korean
]
@
-}
module DearImGui.FontAtlas
( -- * Main types
Raw.Font(..)
, FontSource(..)
-- * Building atlas
, rebuild
-- ** Configuring sources
, ConfigSetup(..)
, fontDataOwnedByAtlas
, fontNo
, sizePixels
, oversampleH
, oversampleV
, pixelSnapH
, glyphExtraSpacing
, glyphOffset
, glyphRanges
, glyphMinAdvanceX
, glyphMaxAdvanceX
, mergeMode
, fontBuilderFlags
, rasterizerMultiply
, ellipsisChar
-- ** Configuring ranges
, Ranges(..)
, RangesBuilderSetup(..)
, addChar
, addText
, addRanges
, addRangesRaw
, pattern Latin
, pattern Korean
, pattern Japanese
, pattern ChineseFull
, pattern ChineseSimplifiedCommon
, pattern Cyrillic
, pattern Thai
, pattern Vietnamese
-- * Lower level types and functions
-- , Raw.FontConfig(..)
-- , Raw.GlyphRanges(..)
, build
, clear
, setupFont
, setupRanges
, withRanges
, withConfig
, addFontFromFileTTF
, addFontFromFileTTF_
)
where
-- base
import Data.Bool (bool)
import Data.Maybe (fromMaybe)
import Foreign
import Foreign.C
-- transformers
import Control.Monad.IO.Class
( MonadIO, liftIO )
-- managed
import Control.Monad.Managed
( MonadManaged, managed )
import qualified Control.Monad.Managed as Managed
-- unlift
import UnliftIO (MonadUnliftIO)
import UnliftIO.Exception (bracket)
-- dear-imgui
import DearImGui.Raw.Font (Font(..))
import qualified DearImGui.Raw.Font as Raw
import DearImGui.Raw.Font.Config (FontConfig(..))
import qualified DearImGui.Raw.Font.Config as FontConfig
import DearImGui.Raw.Font.GlyphRanges (GlyphRanges(..), GlyphRangesBuilder(..))
import qualified DearImGui.Raw.Font.GlyphRanges as GlyphRanges
import DearImGui.Structs (ImVec2(..), ImWchar)
-- | Font setup data
data FontSource
= DefaultFont
| FromTTF FilePath Float (Maybe ConfigSetup) Ranges
-- TODO: FromMemory
-- | Font config monoid interface to be used in 'FontSource'.
--
-- @
-- mergeMode True <> fontNo 1
-- @
newtype ConfigSetup = ConfigSetup
{ applyToConfig :: FontConfig -> IO ()
}
instance Semigroup ConfigSetup where
ConfigSetup f <> ConfigSetup g =
ConfigSetup \fc -> f fc >> g fc
instance Monoid ConfigSetup where
mempty = ConfigSetup (const mempty)
-- | Glyph ranges settings, from presets to builder configuration.
data Ranges
= RangesRaw GlyphRanges
| RangesBuiltin GlyphRanges.Builtin
| RangesBuilder RangesBuilderSetup
-- | Basic Latin, Extended Latin
pattern Latin :: Ranges
pattern Latin = RangesBuiltin GlyphRanges.Latin
-- | Default + Korean characters
pattern Korean :: Ranges
pattern Korean = RangesBuiltin GlyphRanges.Korean
-- | Default + Hiragana, Katakana, Half-Width, Selection of 2999 Ideographs
pattern Japanese :: Ranges
pattern Japanese = RangesBuiltin GlyphRanges.Japanese
-- | Default + Half-Width + Japanese Hiragana/Katakana + full set of about 21000 CJK Unified Ideographs
pattern ChineseFull :: Ranges
pattern ChineseFull = RangesBuiltin GlyphRanges.ChineseFull
-- | Default + Half-Width + Japanese Hiragana/Katakana + set of 2500 CJK Unified Ideographs for common simplified Chinese
pattern ChineseSimplifiedCommon :: Ranges
pattern ChineseSimplifiedCommon = RangesBuiltin GlyphRanges.ChineseSimplifiedCommon
-- | Default + about 400 Cyrillic characters
pattern Cyrillic :: Ranges
pattern Cyrillic = RangesBuiltin GlyphRanges.Cyrillic
-- | Default + Thai characters
pattern Thai :: Ranges
pattern Thai = RangesBuiltin GlyphRanges.Thai
-- | Default + Vietnamese characters
pattern Vietnamese :: Ranges
pattern Vietnamese = RangesBuiltin GlyphRanges.Vietnamese
-- | Ranges builder monoid interface to be executed through 'buildRanges'.
--
-- @
-- addRanges FontRanges.DefaultRanges <> addText "Привет"
-- @
newtype RangesBuilderSetup = RangesBuilderSetup
{ applyToBuilder :: GlyphRangesBuilder -> IO ()
}
instance Semigroup RangesBuilderSetup where
RangesBuilderSetup f <> RangesBuilderSetup g =
RangesBuilderSetup \fc -> f fc >> g fc
instance Monoid RangesBuilderSetup where
mempty = RangesBuilderSetup (const mempty)
-- | Rebuild font atlas with provided configuration
-- and return corresponding structure of font handles
-- to be used with 'withFont'.
--
-- Accepts any 'Traversable' instance, so you are free to use
-- lists, maps or custom structures.
rebuild :: (MonadIO m, Traversable t) => t FontSource -> m (t Font)
rebuild sources = liftIO $ Managed.with action pure
where
action = do
clear
fonts <- traverse setupFont sources
build
return fonts
-- | Reset font atlas, clearing internal data
--
-- Alias for 'Raw.clearFontAtlas'
clear :: (MonadIO m) => m ()
clear = Raw.clearFontAtlas
-- | Build font atlas
--
-- Alias for 'Raw.buildFontAtlas'
build :: (MonadIO m) => m ()
build = Raw.buildFontAtlas
-- | Load a font from TTF file.
--
-- Specify font path and atlas glyph size.
--
-- Use 'Raw.addFontDefault' if you want to retain built-in font too.
--
-- Call 'build' after adding all the fonts,
-- particularly if you're loading them from memory or use custom glyphs.
-- Or stick to `rebuild` function.
--
-- Call backend-specific `CreateFontsTexture` before using 'newFrame'.
addFontFromFileTTF :: MonadIO m
=> FilePath -- ^ Font file path
-> Float -- ^ Font size in pixels
-> Maybe FontConfig -- ^ Configuration data
-> Maybe GlyphRanges -- ^ Glyph ranges to use
-> m (Maybe Font) -- ^ Returns font handle, if added successfully
addFontFromFileTTF font size config ranges = liftIO do
res@(Font ptr) <- withCString font \fontPtr ->
Raw.addFontFromFileTTF
fontPtr
(CFloat size)
(fromMaybe (FontConfig nullPtr) config)
(fromMaybe (GlyphRanges nullPtr) ranges)
pure $
if castPtr ptr == nullPtr
then Nothing
else Just res
-- FIXME: turn off asserts, so it would work
addFontFromFileTTF_ :: MonadIO m
=> FilePath -- ^ Font file path
-> Float -- ^ Font size in pixels
-> m (Maybe Raw.Font) -- ^ Returns font handle, if added successfully
addFontFromFileTTF_ font size =
addFontFromFileTTF font size Nothing Nothing
-- | Load a font with provided configuration, return its handle
-- and defer range builder and config destructors, if needed.
setupFont :: (MonadManaged m) => FontSource -> m Font
setupFont = \case
DefaultFont ->
Raw.addFontDefault
FromTTF path size configSetup ranges -> do
glyphRanges' <- setupRanges ranges
config <- managed (withConfig configSetup)
mFont <- addFontFromFileTTF path size config glyphRanges'
case mFont of
Nothing ->
liftIO . fail $ "Couldn't load font from " <> path
Just font ->
pure font
-- | Configure glyph ranges with provided configuration, return a handle
-- and defer builder destructors, if needed.
setupRanges :: (MonadManaged m) => Ranges -> m (Maybe GlyphRanges)
setupRanges = \case
RangesRaw ranges ->
pure $ Just ranges
RangesBuiltin builtin ->
pure $ GlyphRanges.builtinSetup builtin
RangesBuilder settings -> do
built <- managed $ withRanges settings
pure $ Just built
-- | Perform glyph ranges build based on provided configuration,
-- and execute a computation with built glyph ranges.
withRanges :: (MonadUnliftIO m) => RangesBuilderSetup -> (GlyphRanges -> m a) -> m a
withRanges (RangesBuilderSetup setup) fn =
bracket acquire release execute
where
acquire = do
builder <- GlyphRanges.new
liftIO $ setup builder
rangesVec <- GlyphRanges.buildRangesVector builder
return (rangesVec, builder)
release (rangesVec, builder) = do
GlyphRanges.destroyRangesVector rangesVec
GlyphRanges.destroy builder
execute (rangesVec, _) =
fn (GlyphRanges.fromRangesVector rangesVec)
-- | Configure font config with provided setup,
-- and execute a computation with built object.
-- return its handle and list of resource destructors.
withConfig :: (MonadUnliftIO m) => Maybe ConfigSetup -> (Maybe FontConfig -> m a) -> m a
withConfig mSetup action =
case mSetup of
Nothing ->
action Nothing
Just (ConfigSetup setup) ->
bracket acquire (FontConfig.destroy) (action . Just)
where
acquire = do
config <- FontConfig.new
liftIO $ setup config
return config
-- | Single Unicode character
addChar :: ImWchar -> RangesBuilderSetup
addChar char =
RangesBuilderSetup \builder ->
GlyphRanges.addChar builder char
-- | UTF-8 string
addText :: String -> RangesBuilderSetup
addText str =
RangesBuilderSetup \builder ->
withCString str (GlyphRanges.addText builder)
-- | Existing ranges (as is)
addRangesRaw :: GlyphRanges -> RangesBuilderSetup
addRangesRaw ranges =
RangesBuilderSetup \builder ->
GlyphRanges.addRanges builder ranges
-- | Existing ranges (through settings interface)
addRanges :: Ranges -> RangesBuilderSetup
addRanges = \case
RangesRaw ranges ->
addRangesRaw ranges
RangesBuilder settings ->
settings
RangesBuiltin builtin ->
addRangesRaw (GlyphRanges.getBuiltin builtin)
-- | TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself).
--
-- By default, it is @true@
fontDataOwnedByAtlas :: Bool -> ConfigSetup
fontDataOwnedByAtlas value =
ConfigSetup \fc ->
FontConfig.setFontDataOwnedByAtlas fc (bool 0 1 value)
-- | Index of font within TTF/OTF file.
--
-- By default, it is @0@
fontNo :: Int -> ConfigSetup
fontNo value =
ConfigSetup \fc ->
FontConfig.setFontNo fc (fromIntegral value)
-- | Size in pixels for rasterizer
--
-- More or less maps to the resulting font height.
--
-- Implicitly set by @addFont...@ functions.
sizePixels :: Float -> ConfigSetup
sizePixels value =
ConfigSetup \fc ->
FontConfig.setSizePixels fc (CFloat value)
-- | Rasterize at higher quality for sub-pixel positioning.
--
-- Note: the difference between 2 and 3 is minimal so you can reduce this to 2 to save memory.
-- Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details.
--
-- By default, it is @3@
oversampleH :: Int -> ConfigSetup
oversampleH value =
ConfigSetup \fc ->
FontConfig.setOversampleH fc (fromIntegral value)
-- | Rasterize at higher quality for sub-pixel positioning.
--
-- This is not really useful as we don't use sub-pixel positions on the Y axis.
--
-- By default, it is @1@
oversampleV :: Int -> ConfigSetup
oversampleV value =
ConfigSetup \fc ->
FontConfig.setOversampleV fc (fromIntegral value)
-- | Align every glyph to pixel boundary.
--
-- Useful if you are merging a non-pixel aligned font with the default font.
-- If enabled, you can set OversampleH/V to 1.
--
-- By default, it is @false@
pixelSnapH :: Bool -> ConfigSetup
pixelSnapH value =
ConfigSetup \fc ->
FontConfig.setPixelSnapH fc (bool 0 1 value)
-- | Extra spacing (in pixels) between glyphs.
--
-- Only X axis is supported for now.
--
-- By default, it is @0, 0@
glyphExtraSpacing :: (Float, Float) -> ConfigSetup
glyphExtraSpacing (x, y) =
ConfigSetup \fc ->
Foreign.with (ImVec2 x y) (FontConfig.setGlyphExtraSpacing fc)
-- | Offset all glyphs from this font input.
--
-- By default, it is @0, 0@
glyphOffset :: (Float, Float) -> ConfigSetup
glyphOffset (x, y) =
ConfigSetup \fc ->
Foreign.with (ImVec2 x y) (FontConfig.setGlyphOffset fc)
-- | Pointer to a user-provided list of Unicode range.
--
-- 2 values per range, inclusive. Zero-terminated list.
--
-- THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE.
--
-- By default, it is @NULL@
glyphRanges :: GlyphRanges -> ConfigSetup
glyphRanges value =
ConfigSetup \fc ->
FontConfig.setGlyphRanges fc value
-- | Minimum AdvanceX for glyphs.
--
-- Set Min to align font icons, set both Min/Max to enforce mono-space font.
--
-- By default, it is @0@
glyphMinAdvanceX :: Float -> ConfigSetup
glyphMinAdvanceX value =
ConfigSetup \fc ->
FontConfig.setGlyphMinAdvanceX fc (CFloat value)
-- | Maximum AdvanceX for glyphs.
--
-- By default, it is @FLT_MAX@.
glyphMaxAdvanceX :: Float -> ConfigSetup
glyphMaxAdvanceX value =
ConfigSetup \fc ->
FontConfig.setGlyphMaxAdvanceX fc (CFloat value)
-- | Merge into previous ImFont, so you can combine multiple inputs font into one ImFont.
--
-- e.g. ASCII font + icons + Japanese glyphs.
-- You may want to use @GlyphOffset.y@ when merging font of different heights.
--
-- By default, it is @false@
mergeMode :: Bool -> ConfigSetup
mergeMode value =
ConfigSetup \fc ->
FontConfig.setMergeMode fc (bool 0 1 value)
-- | Settings for custom font GlyphRanges.
--
-- THIS IS BUILDER IMPLEMENTATION DEPENDENT.
--
-- By default, it is @0@. Leave it so if unsure.
fontBuilderFlags :: Int -> ConfigSetup
fontBuilderFlags value =
ConfigSetup \fc ->
FontConfig.setFontBuilderFlags fc (fromIntegral value)
-- | Brighten (>1.0f) or darken (<1.0f) font output.
--
-- Brightening small fonts may be a good workaround to make them more readable.
--
-- By default, it is @1.0f@.
rasterizerMultiply :: Float -> ConfigSetup
rasterizerMultiply value =
ConfigSetup \fc ->
FontConfig.setRasterizerMultiply fc (CFloat value)
-- | Explicitly specify unicode codepoint of ellipsis character.
--
-- When fonts are being merged first specified ellipsis will be used.
--
-- By default, it is @-1@
ellipsisChar :: ImWchar -> ConfigSetup
ellipsisChar value =
ConfigSetup \fc ->
FontConfig.setEllipsisChar fc value

View File

@ -49,6 +49,7 @@ module DearImGui.Raw
-- ** Utilities
, getWindowDrawList
, getWindowPos
, getWindowSize
, getWindowWidth
@ -89,6 +90,7 @@ module DearImGui.Raw
, beginGroup
, endGroup
, setCursorPos
, getCursorScreenPos
, alignTextToFramePadding
-- * Widgets
@ -209,13 +211,12 @@ module DearImGui.Raw
, wantCaptureMouse
, wantCaptureKeyboard
-- * Fonts in default font atlas
, Font(..)
, addFontDefault
, addFontFromFileTTF
, addFontFromMemoryTTF
, buildFontAtlas
, clearFontAtlas
-- * Utilities
-- ** Miscellaneous
, getBackgroundDrawList
, getForegroundDrawList
, imCol32
-- * Types
, module DearImGui.Enums
@ -228,12 +229,15 @@ import Control.Monad.IO.Class
( MonadIO, liftIO )
import Foreign
import Foreign.C
import System.IO.Unsafe
( unsafePerformIO )
-- dear-imgui
import DearImGui.Context
( imguiContext )
import DearImGui.Enums
import DearImGui.Structs
import DearImGui.Raw.DrawList (DrawList(..))
-- inline-c
import qualified Language.C.Inline as C
@ -1264,6 +1268,19 @@ isItemHovered :: (MonadIO m) => m Bool
isItemHovered = liftIO do
(0 /=) <$> [C.exp| bool { IsItemHovered() } |]
-- | Get draw list associated to the current window.
getWindowDrawList :: (MonadIO m) => m DrawList
getWindowDrawList = liftIO do
DrawList <$> [C.exp|
ImDrawList* {
GetWindowDrawList()
}
|]
-- | Get current window position in screen space.
--
-- Useful if you want to do your own drawing via the "DrawList" API.
getWindowPos :: (MonadIO m) => m ImVec2
getWindowPos = liftIO do
C.withPtr_ \ptr ->
@ -1445,6 +1462,21 @@ setCursorPos :: (MonadIO m) => Ptr ImVec2 -> m ()
setCursorPos posPtr = liftIO do
[C.exp| void { SetCursorPos(*$(ImVec2* posPtr)) } |]
-- | Cursor position in absolute coordinates.
--
-- Useful to work with 'DrawList' API.
--
-- Generally top-left == @GetMainViewport()->Pos == (0,0)@ in single viewport mode,
-- and bottom-right == @GetMainViewport()->Pos+Size == io.DisplaySize@ in single-viewport mode.
getCursorScreenPos :: (MonadIO m) => m ImVec2
getCursorScreenPos = liftIO do
C.withPtr_ \ptr ->
[C.block|
void {
*$(ImVec2 * ptr) = GetCursorScreenPos();
}
|]
-- | Modify a style color by pushing to the shared stack. always use this if you modify the style after `newFrame`
--
@ -1523,53 +1555,40 @@ wantCaptureKeyboard :: MonadIO m => m Bool
wantCaptureKeyboard = liftIO do
(0 /=) <$> [C.exp| bool { GetIO().WantCaptureKeyboard } |]
-- | Wraps @ImFont*@.
newtype Font = Font (Ptr ImFont)
addFontDefault :: MonadIO m => m Font
addFontDefault = liftIO do
Font <$> [C.block|
ImFont* {
return GetIO().Fonts->AddFontDefault();
-- | This draw list will be the first rendering one.
--
-- Useful to quickly draw shapes/text behind dear imgui contents.
getBackgroundDrawList :: (MonadIO m) => m DrawList
getBackgroundDrawList = liftIO do
DrawList <$> [C.exp|
ImDrawList* {
GetBackgroundDrawList()
}
|]
addFontFromFileTTF :: MonadIO m => CString -> CFloat -> m Font
addFontFromFileTTF filenamePtr sizePixels = liftIO do
Font <$> [C.block|
ImFont* {
return GetIO().Fonts->AddFontFromFileTTF(
$(char* filenamePtr),
$(float sizePixels));
-- | This draw list will be the last rendered one.
--
-- Useful to quickly draw shapes/text over dear imgui contents.
getForegroundDrawList :: (MonadIO m) => m DrawList
getForegroundDrawList = liftIO do
DrawList <$> [C.exp|
ImDrawList* {
GetForegroundDrawList()
}
|]
-- | Transfer a buffer with TTF data to font atlas builder.
addFontFromMemoryTTF :: MonadIO m => CStringLen -> CFloat -> m Font
addFontFromMemoryTTF (castPtr -> fontDataPtr, fromIntegral -> fontSize) sizePixels = liftIO do
Font <$> [C.block|
ImFont* {
return GetIO().Fonts->AddFontFromMemoryTTF(
$(void* fontDataPtr),
$(int fontSize),
$(float sizePixels)
);
}
|]
buildFontAtlas :: MonadIO m => m ()
buildFontAtlas = liftIO do
[C.block|
void {
GetIO().Fonts->Build();
}
|]
clearFontAtlas :: MonadIO m => m ()
clearFontAtlas = liftIO do
[C.block|
void {
GetIO().Fonts->Clear();
-- | Generate 32-bit encoded colors using DearImgui macros.
--
-- Follows @IMGUI_USE_BGRA_PACKED_COLOR@ define to put bytes in appropriate positions.
imCol32 :: CUChar -> CUChar -> CUChar -> CUChar -> ImU32
imCol32 r g b a = unsafePerformIO
[C.exp|
ImU32 {
IM_COL32(
$(unsigned char r),
$(unsigned char g),
$(unsigned char b),
$(unsigned char a)
)
}
|]

View File

@ -0,0 +1,742 @@
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ViewPatterns #-}
{-| Draw command list
This is the low-level list of polygons that ImGui functions are filling.
At the end of the frame, all command lists are passed to your @ImGuiIO::RenderDrawListFn@ function for rendering.
Each dear imgui window contains its own ImDrawList.
You can use 'getWindowDrawList' to access the current window draw list and draw custom primitives.
You can interleave normal ImGui calls and adding primitives to the current draw list.
In single viewport mode, top-left is == @GetMainViewport()->Pos@ (generally @0,0@),
bottom-right is == @GetMainViewport()->Pos+Size@ (generally io.DisplaySize).
You are totally free to apply whatever transformation matrix to want to the data
(depending on the use of the transformation you may want to apply it to ClipRect as well!).
__Important__: Primitives are always added to the list and not culled (culling is done at higher-level by ImGui functions),
if you use this API a lot consider coarse culling your drawn objects.
-}
module DearImGui.Raw.DrawList
( DrawList(..)
, new
, destroy
-- * Primitives
-- $primitives
, addLine
, addRect
, addRectFilled
, addRectFilledMultiColor
, addQuad
, addQuadFilled
, addTriangle
, addTriangleFilled
, addCircle
, addCircleFilled
, addNgon
, addNgonFilled
, addText_
, addText
, addPolyLine
, addConvexPolyFilled
, addBezierCubic
, addBezierQuadratic
-- ** Image primitives
-- $image
, addImage
, addImageQuad
, addImageRounded
-- * Stateful path API
-- $stateful
, pathClear
, pathLineTo
, pathLineToMergeDuplicate
, pathFillConvex
, pathStroke
, pathArcTo
, pathArcToFast
, pathBezierCubicCurveTo
, pathBezierQuadraticCurveTo
, pathRect
-- * Advanced
-- , addCallback
, addDrawCmd
, cloneOutput
-- * Internal state
, pushClipRect
, pushClipRectFullScreen
, popClipRect
, getClipRectMin
, getClipRectMax
, pushTextureID
, popTextureID
)
where
import Control.Monad.IO.Class
( MonadIO, liftIO )
import Foreign hiding (new)
import Foreign.C
-- dear-imgui
import DearImGui.Context
( imguiContext )
import DearImGui.Enums
import DearImGui.Structs
-- inline-c
import qualified Language.C.Inline as C
-- inline-c-cpp
import qualified Language.C.Inline.Cpp as Cpp
C.context (Cpp.cppCtx <> C.bsCtx <> imguiContext)
C.include "imgui.h"
Cpp.using "namespace ImGui"
-- | A single draw command list.
-- Generally one per window, conceptually you may see this as a dynamic "mesh" builder.
newtype DrawList = DrawList (Ptr ImDrawList)
new :: MonadIO m => m DrawList
new = liftIO do
DrawList <$> [C.block|
ImDrawList* {
return IM_NEW(ImDrawList(GetDrawListSharedData()));
}
|]
destroy :: MonadIO m => DrawList -> m ()
destroy (DrawList drawList) = liftIO do
[C.block|
void {
IM_DELETE($(ImDrawList* drawList));
}
|]
pushClipRect :: MonadIO m => DrawList -> Ptr ImVec2 -> Ptr ImVec2 -> CBool -> m ()
pushClipRect (DrawList drawList) clip_rect_min clip_rect_max intersect_with_current_clip_rect = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->PushClipRect(
*$(ImVec2* clip_rect_min),
*$(ImVec2* clip_rect_max),
$(bool intersect_with_current_clip_rect)
);
}
|]
pushClipRectFullScreen :: MonadIO m => DrawList -> m ()
pushClipRectFullScreen (DrawList drawList) = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->PushClipRectFullScreen();
}
|]
popClipRect :: MonadIO m => DrawList -> m ()
popClipRect (DrawList drawList) = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->PopClipRect();
}
|]
getClipRectMin :: MonadIO m => DrawList -> m ImVec2
getClipRectMin (DrawList drawList) = liftIO do
C.withPtr_ \ptr ->
[C.block|
void {
*$(ImVec2 * ptr) = $(ImDrawList* drawList)->GetClipRectMin();
}
|]
getClipRectMax :: MonadIO m => DrawList -> m ImVec2
getClipRectMax (DrawList drawList) = liftIO do
C.withPtr_ \ptr ->
[C.block|
void {
*$(ImVec2 * ptr) = $(ImDrawList* drawList)->GetClipRectMax();
}
|]
pushTextureID :: MonadIO m => DrawList -> Ptr () -> m ()
pushTextureID (DrawList drawList) userTextureIDPtr = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->PushTextureID(
$(void* userTextureIDPtr)
);
}
|]
popTextureID :: MonadIO m => DrawList -> m ()
popTextureID (DrawList drawList) = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->PopTextureID();
}
|]
{- $primitives
- For rectangular primitives, @p_min@ and @p_max@ represent the upper-left and lower-right corners.
- For circle primitives, use @num_segments == 0@ to automatically calculate tessellation (preferred).
In older versions (until Dear ImGui 1.77) the 'addCircle' functions defaulted to num_segments == 12.
In future versions we will use textures to provide cheaper and higher-quality circles.
Use 'addNgon' and 'addNgonFilled' functions if you need to guaranteed a specific number of sides.
-}
addLine :: MonadIO m => DrawList -> Ptr ImVec2 -> Ptr ImVec2 -> ImU32 -> CFloat -> m ()
addLine (DrawList drawList) p1 p2 col thickness = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddLine(
*$(ImVec2* p1),
*$(ImVec2* p2),
$(ImU32 col),
$(float thickness)
);
}
|]
addRect :: MonadIO m => DrawList -> Ptr ImVec2 -> Ptr ImVec2 -> ImU32 -> CFloat -> ImDrawFlags -> CFloat -> m ()
addRect (DrawList drawList) p_min p_max col rounding flags thickness = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddRect(
*$(ImVec2* p_min),
*$(ImVec2* p_max),
$(ImU32 col),
$(float rounding),
$(ImDrawFlags flags),
$(float thickness)
);
}
|]
addRectFilled :: MonadIO m => DrawList -> Ptr ImVec2 -> Ptr ImVec2 -> ImU32 -> CFloat -> ImDrawFlags -> m ()
addRectFilled (DrawList drawList) p_min p_max col rounding flags = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddRectFilled(
*$(ImVec2* p_min),
*$(ImVec2* p_max),
$(ImU32 col),
$(float rounding),
$(ImDrawFlags flags)
);
}
|]
addRectFilledMultiColor :: MonadIO m => DrawList -> Ptr ImVec2 -> Ptr ImVec2 -> ImU32 -> ImU32 -> ImU32 -> ImU32 -> m ()
addRectFilledMultiColor (DrawList drawList) p_min p_max col_upr_left col_upr_right col_bot_right col_bot_left = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddRectFilledMultiColor(
*$(ImVec2* p_min),
*$(ImVec2* p_max),
$(ImU32 col_upr_left),
$(ImU32 col_upr_right),
$(ImU32 col_bot_right),
$(ImU32 col_bot_left)
);
}
|]
addQuad :: MonadIO m => DrawList -> Ptr ImVec2 -> Ptr ImVec2 -> Ptr ImVec2 -> Ptr ImVec2 -> ImU32 -> CFloat -> m ()
addQuad (DrawList drawList) p1 p2 p3 p4 col thickness = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddQuad(
*$(ImVec2* p1),
*$(ImVec2* p2),
*$(ImVec2* p3),
*$(ImVec2* p4),
$(ImU32 col),
$(float thickness)
);
}
|]
addQuadFilled :: MonadIO m => DrawList -> Ptr ImVec2 -> Ptr ImVec2 -> Ptr ImVec2 -> Ptr ImVec2 -> ImU32 -> m ()
addQuadFilled (DrawList drawList) p1 p2 p3 p4 col = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddQuadFilled(
*$(ImVec2* p1),
*$(ImVec2* p2),
*$(ImVec2* p3),
*$(ImVec2* p4),
$(ImU32 col)
);
}
|]
addTriangle :: MonadIO m => DrawList -> Ptr ImVec2 -> Ptr ImVec2 -> Ptr ImVec2 -> ImU32 -> CFloat -> m ()
addTriangle (DrawList drawList) p1 p2 p3 col thickness = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddTriangle(
*$(ImVec2* p1),
*$(ImVec2* p2),
*$(ImVec2* p3),
$(ImU32 col),
$(float thickness)
);
}
|]
addTriangleFilled :: MonadIO m => DrawList -> Ptr ImVec2 -> Ptr ImVec2 -> Ptr ImVec2 -> ImU32 -> m ()
addTriangleFilled (DrawList drawList) p1 p2 p3 col = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddTriangleFilled(
*$(ImVec2* p1),
*$(ImVec2* p2),
*$(ImVec2* p3),
$(ImU32 col)
);
}
|]
addCircle :: MonadIO m => DrawList -> Ptr ImVec2 -> CFloat -> ImU32 -> CInt -> CFloat -> m ()
addCircle (DrawList drawList) center radius col num_segments thickness = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddCircle(
*$(ImVec2* center),
$(float radius),
$(ImU32 col),
$(int num_segments),
$(float thickness)
);
}
|]
addCircleFilled :: MonadIO m => DrawList -> Ptr ImVec2 -> CFloat -> ImU32 -> CInt -> m ()
addCircleFilled (DrawList drawList) center radius col num_segments = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddCircleFilled(
*$(ImVec2* center),
$(float radius),
$(ImU32 col),
$(int num_segments)
);
}
|]
addNgon :: MonadIO m => DrawList -> Ptr ImVec2 -> CFloat -> ImU32 -> CInt -> CFloat -> m ()
addNgon (DrawList drawList) center radius col num_segments thickness = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddNgon(
*$(ImVec2* center),
$(float radius),
$(ImU32 col),
$(int num_segments),
$(float thickness)
);
}
|]
addNgonFilled :: MonadIO m => DrawList -> Ptr ImVec2 -> CFloat -> ImU32 -> CInt -> m ()
addNgonFilled (DrawList drawList) center radius col num_segments = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddNgonFilled(
*$(ImVec2* center),
$(float radius),
$(ImU32 col),
$(int num_segments)
);
}
|]
addText_ :: MonadIO m => DrawList -> Ptr ImVec2 -> ImU32 -> CString -> CString -> m ()
addText_ (DrawList drawList) pos col text_begin text_end = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddText(
*$(ImVec2* pos),
$(ImU32 col),
$(char* text_begin),
$(char* text_end)
);
}
|]
addText :: MonadIO m => DrawList -> Ptr ImFont -> CFloat -> Ptr ImVec2 -> ImU32 -> CString -> CString -> CFloat -> Ptr ImVec4 -> m ()
addText (DrawList drawList) fontPtr font_size pos col text_begin text_end wrap_width cpu_fine_clip_rect = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddText(
$(ImFont* fontPtr),
$(float font_size),
*$(ImVec2* pos),
$(ImU32 col),
$(char* text_begin),
$(char* text_end),
$(float wrap_width),
$(ImVec4* cpu_fine_clip_rect)
);
}
|]
addPolyLine :: MonadIO m => DrawList -> Ptr ImVec2 -> CInt -> ImU32 -> ImDrawFlags -> CFloat -> m ()
addPolyLine (DrawList drawList) points num_points col flags thickness = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddPolyline(
$(ImVec2* points),
$(int num_points),
$(ImU32 col),
$(ImDrawFlags flags),
$(float thickness)
);
}
|]
addConvexPolyFilled :: MonadIO m => DrawList -> Ptr ImVec2 -> CInt -> ImU32 -> m ()
addConvexPolyFilled (DrawList drawList) points num_points col = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddConvexPolyFilled(
$(ImVec2* points),
$(int num_points),
$(ImU32 col)
);
}
|]
addBezierCubic
:: MonadIO m
=> DrawList
-> Ptr ImVec2 -> Ptr ImVec2 -> Ptr ImVec2 -> Ptr ImVec2 -- Positions (control points)
-> ImU32
-> CFloat
-> CInt
-> m ()
addBezierCubic (DrawList drawList) p1 p2 p3 p4 col thickness numSegments = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddBezierCubic(
*$(ImVec2* p1),
*$(ImVec2* p2),
*$(ImVec2* p3),
*$(ImVec2* p4),
$(ImU32 col),
$(float thickness),
$(int numSegments)
);
}
|]
addBezierQuadratic
:: MonadIO m
=> DrawList
-> Ptr ImVec2 -> Ptr ImVec2 -> Ptr ImVec2 -- Positions (control points)
-> ImU32
-> CFloat
-> CInt
-> m ()
addBezierQuadratic (DrawList drawList) p1 p2 p3 col thickness numSegments = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddBezierQuadratic(
*$(ImVec2* p1),
*$(ImVec2* p2),
*$(ImVec2* p3),
$(ImU32 col),
$(float thickness),
$(int numSegments)
);
}
|]
{- $image
* Read FAQ to understand what @ImTextureID@ is.
* @p_min@ and @p_max@ represent the upper-left and lower-right corners of the rectangle.
* @uv_min@ and @uv_max@ represent the normalized texture coordinates to use for those corners.
Using @(0,0)->(1,1)@ texture coordinates will generally display the entire texture.
-}
addImage
:: MonadIO m
=> DrawList
-> Ptr ()
-> Ptr ImVec2 -> Ptr ImVec2 -- Positions
-> Ptr ImVec2 -> Ptr ImVec2 -- UVs
-> ImU32
-> m ()
addImage (DrawList drawList) userTextureIDPtr p_min p_max uv_min uv_max col = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddImage(
$(void* userTextureIDPtr),
*$(ImVec2* p_min),
*$(ImVec2* p_max),
*$(ImVec2* uv_min),
*$(ImVec2* uv_max),
$(ImU32 col)
);
}
|]
addImageQuad
:: MonadIO m
=> DrawList
-> Ptr ()
-> Ptr ImVec2 -> Ptr ImVec2 -> Ptr ImVec2 -> Ptr ImVec2 -- Positions
-> Ptr ImVec2 -> Ptr ImVec2 -> Ptr ImVec2 -> Ptr ImVec2 -- UVs
-> ImU32
-> m ()
addImageQuad (DrawList drawList) userTextureIDPtr p1 p2 p3 p4 uv1 uv2 uv3 uv4 col = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddImageQuad(
$(void* userTextureIDPtr),
*$(ImVec2* p1),
*$(ImVec2* p2),
*$(ImVec2* p3),
*$(ImVec2* p4),
*$(ImVec2* uv1),
*$(ImVec2* uv2),
*$(ImVec2* uv3),
*$(ImVec2* uv4),
$(ImU32 col)
);
}
|]
addImageRounded
:: MonadIO m
=> DrawList
-> Ptr ()
-> Ptr ImVec2 -> Ptr ImVec2 -- Positions
-> Ptr ImVec2 -> Ptr ImVec2 -- UVs
-> ImU32
-> CFloat
-> ImDrawFlags
-> m ()
addImageRounded (DrawList drawList) userTextureIDPtr p_min p_max uv_min uv_max col rounding flags = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddImageRounded(
$(void* userTextureIDPtr),
*$(ImVec2* p_min),
*$(ImVec2* p_max),
*$(ImVec2* uv_min),
*$(ImVec2* uv_max),
$(ImU32 col),
$(float rounding),
$(ImDrawFlags flags)
);
}
|]
{- $stateful
Add points then finish with 'pathFillConvex' or 'pathStroke'.
-}
pathClear :: MonadIO m => DrawList -> m ()
pathClear (DrawList drawList) = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->PathClear();
}
|]
pathLineTo :: MonadIO m => DrawList -> Ptr ImVec2 -> m ()
pathLineTo (DrawList drawList) pos = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->PathLineTo(
*$(ImVec2* pos)
);
}
|]
pathLineToMergeDuplicate :: MonadIO m => DrawList -> Ptr ImVec2 -> m ()
pathLineToMergeDuplicate (DrawList drawList) pos = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->PathLineToMergeDuplicate(
*$(ImVec2* pos)
);
}
|]
-- | Note: Anti-aliased filling requires points to be in clockwise order.
pathFillConvex :: MonadIO m => DrawList -> ImU32 -> m ()
pathFillConvex (DrawList drawList) col = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->PathFillConvex(
$(ImU32 col)
);
}
|]
pathStroke :: MonadIO m => DrawList -> ImU32 -> ImDrawFlags -> CFloat -> m ()
pathStroke (DrawList drawList) col flags thickness = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->PathStroke(
$(ImU32 col),
$(ImDrawFlags flags),
$(float thickness)
);
}
|]
pathArcTo :: MonadIO m => DrawList -> Ptr ImVec2 -> CFloat -> CFloat -> CFloat -> CInt -> m ()
pathArcTo (DrawList drawList) center radius a_min a_max num_segments = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->PathArcTo(
*$(ImVec2* center),
$(float radius),
$(float a_min),
$(float a_max),
$(int num_segments)
);
}
|]
-- | Use precomputed angles for a 12 steps circle.
pathArcToFast :: MonadIO m => DrawList -> Ptr ImVec2 -> CFloat -> CInt -> CInt -> m ()
pathArcToFast (DrawList drawList) center radius a_min_of_12 a_max_of_12 = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->PathArcToFast(
*$(ImVec2* center),
$(float radius),
$(int a_min_of_12),
$(int a_max_of_12)
);
}
|]
pathBezierCubicCurveTo
:: MonadIO m
=> DrawList
-> Ptr ImVec2
-> Ptr ImVec2
-> Ptr ImVec2
-> CInt
-> m ()
pathBezierCubicCurveTo (DrawList drawList) p1 p2 p3 num_segments = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->PathBezierCubicCurveTo(
*$(ImVec2* p1),
*$(ImVec2* p2),
*$(ImVec2* p3),
$(int num_segments)
);
}
|]
pathBezierQuadraticCurveTo
:: MonadIO m
=> DrawList
-> Ptr ImVec2
-> Ptr ImVec2
-> CInt
-> m ()
pathBezierQuadraticCurveTo (DrawList drawList) p1 p2 num_segments = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->PathBezierQuadraticCurveTo(
*$(ImVec2* p1),
*$(ImVec2* p2),
$(int num_segments)
);
}
|]
pathRect :: MonadIO m => DrawList -> Ptr ImVec2 -> Ptr ImVec2 -> CFloat -> ImDrawFlags -> m ()
pathRect (DrawList drawList) rect_min rect_max rounding flags = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->PathRect(
*$(ImVec2* rect_min),
*$(ImVec2* rect_max),
$(float rounding),
$(ImDrawFlags flags)
);
}
|]
-- | This is useful if you need to forcefully create a new draw call (to allow for dependent rendering / blending).
-- Otherwise primitives are merged into the same draw-call as much as possible.
addDrawCmd :: MonadIO m => DrawList -> m ()
addDrawCmd (DrawList drawList) = liftIO do
[C.block|
void {
$(ImDrawList* drawList)->AddDrawCmd();
}
|]
-- | Create a clone of the CmdBuffer/IdxBuffer/VtxBuffer.
cloneOutput :: MonadIO m => DrawList -> m DrawList
cloneOutput (DrawList drawList) = liftIO do
DrawList <$> [C.block|
ImDrawList* {
return $(ImDrawList* drawList)->CloneOutput();
}
|]

141
src/DearImGui/Raw/Font.hs Normal file
View File

@ -0,0 +1,141 @@
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ViewPatterns #-}
{-| Fonts
It includes default atlas management, font configuration and glyph ranges.
-}
module DearImGui.Raw.Font
( -- * Types
Font(..)
, GlyphRanges(..)
-- * Adding fonts
, addFontDefault
, addFontFromFileTTF
, addFontFromMemoryTTF
-- * Using fonts
, pushFont
, popFont
-- * Atlas management
, clearFontAtlas
, buildFontAtlas
)
where
-- base
import Control.Monad.IO.Class
( MonadIO, liftIO )
import Foreign ( Ptr, castPtr )
import Foreign.C
-- dear-imgui
import DearImGui.Context
( imguiContext )
import DearImGui.Structs
import DearImGui.Raw.Font.Config
( FontConfig(..) )
import DearImGui.Raw.Font.GlyphRanges
( GlyphRanges(..) )
-- inline-c
import qualified Language.C.Inline as C
-- inline-c-cpp
import qualified Language.C.Inline.Cpp as Cpp
C.context (Cpp.cppCtx <> C.bsCtx <> imguiContext)
C.include "imgui.h"
Cpp.using "namespace ImGui"
-- | Font runtime data handle
--
-- Wraps @ImFont*@.
newtype Font = Font (Ptr ImFont)
-- | Add the default font (@ProggyClean.ttf@, 13 px) to the atlas.
addFontDefault :: MonadIO m
=> m Font -- ^ Returns font handle for future usage
addFontDefault = liftIO do
Font <$> [C.block|
ImFont* {
return GetIO().Fonts->AddFontDefault();
}
|]
-- | Add a custom OTF/TTF font from a file.
addFontFromFileTTF :: MonadIO m
=> CString -- ^ Font file path
-> CFloat -- ^ Font size in pixels
-> FontConfig -- ^ Configuration data
-> GlyphRanges -- ^ Glyph ranges to use
-> m Font -- ^ Returns font handle for future usage
addFontFromFileTTF filenamePtr sizePixels (FontConfig fontConfig) (GlyphRanges glyphRanges) = liftIO do
Font <$> [C.block|
ImFont* {
return GetIO().Fonts->AddFontFromFileTTF(
$(char* filenamePtr),
$(float sizePixels),
$(ImFontConfig* fontConfig),
$(ImWchar* glyphRanges));
}
|]
-- | Transfer a buffer with TTF data to font atlas builder.
addFontFromMemoryTTF :: MonadIO m => CStringLen -> CFloat -> FontConfig -> GlyphRanges -> m Font
addFontFromMemoryTTF (castPtr -> fontDataPtr, fromIntegral -> fontSize) sizePixels (FontConfig fontConfig) (GlyphRanges glyphRanges) = liftIO do
Font <$> [C.block|
ImFont* {
return GetIO().Fonts->AddFontFromMemoryTTF(
$(void* fontDataPtr),
$(int fontSize),
$(float sizePixels),
$(ImFontConfig* fontConfig),
$(ImWchar* glyphRanges)
);
}
|]
-- | Pushes a font into the parameters stack,
-- so ImGui would render following text using it.
pushFont :: MonadIO m => Font -> m ()
pushFont (Font font) = liftIO do
[C.exp| void { PushFont($(ImFont* font)); } |]
-- | Pops a font pushed into the parameters stack
--
-- Should be called only after a corresponding 'pushFont' call.
popFont :: MonadIO m => m ()
popFont = liftIO do
[C.exp| void { PopFont(); } |]
-- | Explicitly build pixels data for the atlas.
buildFontAtlas :: MonadIO m => m ()
buildFontAtlas = liftIO do
[C.block|
void {
GetIO().Fonts->Build();
}
|]
-- | Clear all font atlas input and output data
clearFontAtlas :: MonadIO m => m ()
clearFontAtlas = liftIO do
[C.block|
void {
GetIO().Fonts->Clear();
}
|]

View File

@ -0,0 +1,256 @@
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ViewPatterns #-}
{-| Font configuration
IO functions to modify font config values.
-}
module DearImGui.Raw.Font.Config
( FontConfig(..)
, new
, destroy
-- * Changing settings
, setFontDataOwnedByAtlas
, setFontNo
, setSizePixels
, setOversampleH
, setOversampleV
, setPixelSnapH
, setGlyphExtraSpacing
, setGlyphOffset
, setGlyphRanges
, setGlyphMinAdvanceX
, setGlyphMaxAdvanceX
, setMergeMode
, setFontBuilderFlags
, setRasterizerMultiply
, setEllipsisChar
)
where
-- base
import Control.Monad.IO.Class
( MonadIO, liftIO )
import Foreign ( Ptr )
import Foreign.C
-- dear-imgui
import DearImGui.Context
( imguiContext )
import DearImGui.Structs
import DearImGui.Raw.Font.GlyphRanges
( GlyphRanges(..) )
-- inline-c
import qualified Language.C.Inline as C
-- inline-c-cpp
import qualified Language.C.Inline.Cpp as Cpp
C.context (Cpp.cppCtx <> C.bsCtx <> imguiContext)
C.include "imgui.h"
Cpp.using "namespace ImGui"
-- | Font configuration data handle
--
-- Wraps @ImFontConfig*@.
newtype FontConfig = FontConfig (Ptr ImFontConfig)
-- | Create an instance of config
new :: MonadIO m => m FontConfig
new = liftIO do
FontConfig <$> [C.block|
ImFontConfig* {
return IM_NEW(ImFontConfig);
}
|]
-- | Destroy an instance of config
--
-- Should be used __after__ font atlas building.
destroy :: MonadIO m => FontConfig -> m ()
destroy (FontConfig config) = liftIO do
[C.block|
void {
IM_DELETE($(ImFontConfig* config));
}
|]
-- | TTF/OTF data ownership taken by the container ImFontAtlas (will delete memory itself).
--
-- By default, it is @true@
setFontDataOwnedByAtlas :: MonadIO m => FontConfig -> CBool -> m ()
setFontDataOwnedByAtlas (FontConfig fc) value = liftIO do
[C.block|
void {
$(ImFontConfig* fc)->FontDataOwnedByAtlas = $(bool value);
}
|]
-- | Index of font within TTF/OTF file
--
-- By default, it is @0@
setFontNo :: MonadIO m => FontConfig -> CInt -> m ()
setFontNo (FontConfig fc) value = liftIO do
[C.block|
void {
$(ImFontConfig* fc)->FontNo = $(int value);
}
|]
-- | Size in pixels for rasterizer (more or less maps to the resulting font height).
--
-- Implicitly set by @addFont...@ functions.
setSizePixels :: MonadIO m => FontConfig -> CFloat -> m ()
setSizePixels (FontConfig fc) value = liftIO do
[C.block|
void {
$(ImFontConfig* fc)->SizePixels = $(float value);
}
|]
-- | Rasterize at higher quality for sub-pixel positioning. Note the difference between 2 and 3 is minimal so you can reduce this to 2 to save memory. Read https://github.com/nothings/stb/blob/master/tests/oversample/README.md for details.
--
-- By default, it is @3@
setOversampleH :: MonadIO m => FontConfig -> CInt -> m ()
setOversampleH (FontConfig fc) value = liftIO do
[C.block|
void {
$(ImFontConfig* fc)->OversampleH = $(int value);
}
|]
-- | Rasterize at higher quality for sub-pixel positioning. This is not really useful as we don't use sub-pixel positions on the Y axis.
--
-- By default, it is @1@
setOversampleV :: MonadIO m => FontConfig -> CInt -> m ()
setOversampleV (FontConfig fc) value = liftIO do
[C.block|
void {
$(ImFontConfig* fc)->OversampleV = $(int value);
}
|]
-- | Align every glyph to pixel boundary. Useful e.g. if you are merging a non-pixel aligned font with the default font. If enabled, you can set OversampleH/V to 1.
--
-- By default, it is @false@
setPixelSnapH :: MonadIO m => FontConfig -> CBool -> m ()
setPixelSnapH (FontConfig fc) value = liftIO do
[C.block|
void {
$(ImFontConfig* fc)->PixelSnapH = $(bool value);
}
|]
-- | Extra spacing (in pixels) between glyphs. Only X axis is supported for now.
--
-- By default, it is @0, 0@
setGlyphExtraSpacing :: MonadIO m => FontConfig -> Ptr ImVec2 -> m ()
setGlyphExtraSpacing (FontConfig fc) value = liftIO do
[C.block|
void {
$(ImFontConfig* fc)->GlyphExtraSpacing = *$(ImVec2* value);
}
|]
-- | Offset all glyphs from this font input.
--
-- By default, it is @0, 0@
setGlyphOffset :: MonadIO m => FontConfig -> Ptr ImVec2 -> m ()
setGlyphOffset (FontConfig fc) value = liftIO do
[C.block|
void {
$(ImFontConfig* fc)->GlyphOffset = *$(ImVec2* value);
}
|]
-- | Pointer to a user-provided list of Unicode range (2 value per range, values are inclusive, zero-terminated list). THE ARRAY DATA NEEDS TO PERSIST AS LONG AS THE FONT IS ALIVE.
--
-- By default, it is @NULL@
setGlyphRanges :: MonadIO m => FontConfig -> GlyphRanges -> m ()
setGlyphRanges (FontConfig fc) (GlyphRanges value) = liftIO do
[C.block|
void {
$(ImFontConfig* fc)->GlyphRanges = $(ImWchar* value);
}
|]
-- | Minimum AdvanceX for glyphs, set Min to align font icons, set both Min/Max to enforce mono-space font
--
-- By default, it is @0@
setGlyphMinAdvanceX :: MonadIO m => FontConfig -> CFloat -> m ()
setGlyphMinAdvanceX (FontConfig fc) value = liftIO do
[C.block|
void {
$(ImFontConfig* fc)->GlyphMinAdvanceX = $(float value);
}
|]
-- | Maximum AdvanceX for glyphs
--
-- By default, it is @FLT_MAX@
setGlyphMaxAdvanceX :: MonadIO m => FontConfig -> CFloat -> m ()
setGlyphMaxAdvanceX (FontConfig fc) value = liftIO do
[C.block|
void {
$(ImFontConfig* fc)->GlyphMaxAdvanceX = $(float value);
}
|]
-- | Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs). You may want to use GlyphOffset.y when merge font of different heights.
--
-- By default, it is @false@
setMergeMode :: MonadIO m => FontConfig -> CBool -> m ()
setMergeMode (FontConfig fc) value = liftIO do
[C.block|
void {
$(ImFontConfig* fc)->MergeMode = $(bool value);
}
|]
-- | Settings for custom font builder.
-- THIS IS BUILDER IMPLEMENTATION DEPENDENT.
--
-- By default, it is @0@. Leave it so if unsure.
setFontBuilderFlags :: MonadIO m => FontConfig -> CUInt -> m ()
setFontBuilderFlags (FontConfig fc) value = liftIO do
[C.block|
void {
$(ImFontConfig* fc)->FontBuilderFlags = $(unsigned int value);
}
|]
-- | Brighten (>1.0f) or darken (<1.0f) font output.
-- Brightening small fonts may be a good workaround to make them more readable.
--
-- By default, it is @1.0f@
setRasterizerMultiply :: MonadIO m => FontConfig -> CFloat -> m ()
setRasterizerMultiply (FontConfig fc) value = liftIO do
[C.block|
void {
$(ImFontConfig* fc)->RasterizerMultiply = $(float value);
}
|]
-- | Explicitly specify unicode codepoint of ellipsis character. When fonts are being merged first specified ellipsis will be used.
--
-- By default, it is @-1@
setEllipsisChar :: MonadIO m => FontConfig -> ImWchar -> m ()
setEllipsisChar (FontConfig fc) value = liftIO do
[C.block|
void {
$(ImFontConfig* fc)->EllipsisChar = $(ImWchar value);
}
|]

View File

@ -0,0 +1,295 @@
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-| Font glyph ranges builder
Helper to build glyph ranges from text/string data.
Feed your application strings/characters to it then call 'buildRanges'.
Low-level example of usage:
@
-- import ImGui.Fonts
-- import ImGui.Raw.GlyphRangesBuilder as GRB
builder <- GRB.new
GRB.addRanges builder getGlyphRangesDefault
liftIO $ withCString "Привет" $ GRB.addText builder
rangesVec <- GRB.buildRanges builder
let ranges = GRB.fromRangesVector rangesVec
addFontFromFileTTF'
"./imgui/misc/fonts/DroidSans.ttf" 12
Nothing
(Just ranges)
-- it is strictly necessary to explicitly build the atlas
buildFontAtlas
-- resource destruction comes only after the building
GRB.destroyRangesVector rangesVec
GRB.destroy builder
@
-}
module DearImGui.Raw.Font.GlyphRanges
( GlyphRanges(..)
-- * Built-in ranges
, Builtin(..)
, getBuiltin
, builtinSetup
-- * Preparing a builder
, GlyphRangesBuilder(..)
, new
, destroy
, addChar
, addText
, addRanges
-- * Extracting data
, GlyphRangesVector(..)
, buildRangesVector
, fromRangesVector
, destroyRangesVector
)
where
-- base
import Control.Monad.IO.Class
( MonadIO, liftIO )
import Foreign ( Ptr )
import Foreign.C
import System.IO.Unsafe (unsafePerformIO)
-- dear-imgui
import DearImGui.Context
( imguiContext )
import DearImGui.Structs
-- inline-c
import qualified Language.C.Inline as C
-- inline-c-cpp
import qualified Language.C.Inline.Cpp as Cpp
C.context (Cpp.cppCtx <> C.bsCtx <> imguiContext)
C.include "imgui.h"
Cpp.using "namespace ImGui"
-- | Glyph ranges handle
--
-- Wraps @ImWchar*@.
newtype GlyphRanges = GlyphRanges (Ptr ImWchar)
-- | Builtin glyph ranges tags.
data Builtin
= Latin
| Korean
| Japanese
| ChineseFull
| ChineseSimplifiedCommon
| Cyrillic
| Thai
| Vietnamese
deriving (Eq, Ord, Show, Enum, Bounded)
-- | Get builtin glyph ranges from a tag.
getBuiltin :: Builtin -> GlyphRanges
getBuiltin = \case
Latin -> getGlyphRangesDefault
Korean -> getGlyphRangesKorean
Japanese -> getGlyphRangesJapanese
ChineseFull -> getGlyphRangesChineseFull
ChineseSimplifiedCommon -> getGlyphRangesChineseSimplifiedCommon
Cyrillic -> getGlyphRangesCyrillic
Thai -> getGlyphRangesThai
Vietnamese -> getGlyphRangesVietnamese
-- | Special case of @getBuiltin@, but for font source setup.
builtinSetup :: Builtin -> Maybe GlyphRanges
builtinSetup = \case
Latin -> Nothing
others -> Just (getBuiltin others)
-- | Basic Latin, Extended Latin
getGlyphRangesDefault :: GlyphRanges
getGlyphRangesDefault = unsafePerformIO do
GlyphRanges <$> [C.block|
const ImWchar* {
return GetIO().Fonts->GetGlyphRangesDefault();
}
|]
-- | Default + Korean characters
getGlyphRangesKorean :: GlyphRanges
getGlyphRangesKorean = unsafePerformIO do
GlyphRanges <$> [C.block|
const ImWchar* {
return GetIO().Fonts->GetGlyphRangesKorean();
}
|]
-- | Default + Hiragana, Katakana, Half-Width, Selection of 2999 Ideographs
getGlyphRangesJapanese :: GlyphRanges
getGlyphRangesJapanese = unsafePerformIO do
GlyphRanges <$> [C.block|
const ImWchar* {
return GetIO().Fonts->GetGlyphRangesJapanese();
}
|]
-- | Default + Half-Width + Japanese Hiragana/Katakana + full set of about 21000 CJK Unified Ideographs
getGlyphRangesChineseFull :: GlyphRanges
getGlyphRangesChineseFull = unsafePerformIO do
GlyphRanges <$> [C.block|
const ImWchar* {
return GetIO().Fonts->GetGlyphRangesChineseFull();
}
|]
-- | Default + Half-Width + Japanese Hiragana/Katakana + set of 2500 CJK Unified Ideographs for common simplified Chinese
getGlyphRangesChineseSimplifiedCommon :: GlyphRanges
getGlyphRangesChineseSimplifiedCommon = unsafePerformIO do
GlyphRanges <$> [C.block|
const ImWchar* {
return GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon();
}
|]
-- | Default + about 400 Cyrillic characters
getGlyphRangesCyrillic :: GlyphRanges
getGlyphRangesCyrillic = unsafePerformIO do
GlyphRanges <$> [C.block|
const ImWchar* {
return GetIO().Fonts->GetGlyphRangesCyrillic();
}
|]
-- | Default + Thai characters
getGlyphRangesThai :: GlyphRanges
getGlyphRangesThai = unsafePerformIO do
GlyphRanges <$> [C.block|
const ImWchar* {
return GetIO().Fonts->GetGlyphRangesThai();
}
|]
-- | Default + Vietnamese characters
getGlyphRangesVietnamese :: GlyphRanges
getGlyphRangesVietnamese = unsafePerformIO do
GlyphRanges <$> [C.block|
const ImWchar* {
return GetIO().Fonts->GetGlyphRangesVietnamese();
}
|]
-- | Glyph ranges builder handle
--
-- Wraps @ImFontGlyphRangesBuilder*@.
newtype GlyphRangesBuilder = GlyphRangesBuilder (Ptr ImFontGlyphRangesBuilder)
-- | Glyph ranges vector handle to keep builder output
--
-- Wraps @ImVector<ImWchar>*@.
newtype GlyphRangesVector = GlyphRangesVector (Ptr ())
-- | Create an instance of builder
new :: MonadIO m => m GlyphRangesBuilder
new = liftIO do
GlyphRangesBuilder <$> [C.block|
ImFontGlyphRangesBuilder* {
return IM_NEW(ImFontGlyphRangesBuilder);
}
|]
-- | Destroy an instance of builder
--
-- Should be used __after__ font atlas building.
destroy :: MonadIO m => GlyphRangesBuilder -> m ()
destroy (GlyphRangesBuilder builder) = liftIO do
[C.block|
void {
IM_DELETE($(ImFontGlyphRangesBuilder* builder));
}
|]
-- | Add character
addChar :: MonadIO m => GlyphRangesBuilder -> ImWchar -> m ()
addChar (GlyphRangesBuilder builder) wChar = liftIO do
[C.block|
void {
$(ImFontGlyphRangesBuilder* builder)->AddChar($(ImWchar wChar));
}
|]
-- | Add string (each character of the UTF-8 string are added)
addText :: MonadIO m => GlyphRangesBuilder -> CString -> m ()
addText (GlyphRangesBuilder builder) string = liftIO do
[C.block|
void {
$(ImFontGlyphRangesBuilder* builder)->AddText($(char* string));
}
|]
-- FIXME: the function uses 'const char* text_end = NULL' parameter,
-- which is pointer for the line ending. It is low level, though it
-- could be utilized for string length parameter.
-- | Add ranges, e.g. 'addRanges builder getGlyphRangesDefault'
-- to force add all of ASCII/Latin+Ext
addRanges :: MonadIO m => GlyphRangesBuilder -> GlyphRanges -> m()
addRanges (GlyphRangesBuilder builder) (GlyphRanges ranges) = liftIO do
[C.block|
void {
$(ImFontGlyphRangesBuilder* builder)->AddRanges($(ImWchar* ranges));
}
|]
-- | Build new ranges and create ranges vector instance,
-- containing them
buildRangesVector :: MonadIO m => GlyphRangesBuilder -> m (GlyphRangesVector)
buildRangesVector (GlyphRangesBuilder builder) = liftIO do
GlyphRangesVector <$> [C.block|
void* {
ImVector<ImWchar>* ranges = IM_NEW(ImVector<ImWchar>);
$(ImFontGlyphRangesBuilder* builder)->BuildRanges(ranges);
return ranges;
}
|]
-- | Extract glyph ranges from a vector
--
-- Should be used __before__ vector destruction.
fromRangesVector :: GlyphRangesVector -> GlyphRanges
fromRangesVector (GlyphRangesVector vecPtr) = unsafePerformIO do
GlyphRanges <$> [C.block|
ImWchar* {
return ((ImVector<ImWchar>*) $(void* vecPtr))->Data;
}
|]
-- | Destroy a ranges vector instance
--
-- Should be used __after__ font atlas building.
destroyRangesVector :: MonadIO m => GlyphRangesVector -> m ()
destroyRangesVector (GlyphRangesVector vecPtr) = liftIO do
[C.block|
void {
IM_DELETE(((ImVector<ImWchar>*) $(void* vecPtr)));
}
|]

137
src/DearImGui/Raw/IO.hs Normal file
View File

@ -0,0 +1,137 @@
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ViewPatterns #-}
{-| Main configuration and I/O between your application and ImGui
-}
module DearImGui.Raw.IO
( setIniFilename
, setLogFilename
, setMouseDoubleClickMaxDist
, setMouseDoubleClickTime
, setMouseDragThreshold
, setKeyRepeatDelay
, setKeyRepeatRate
, setUserData
) where
-- TODO: add exports
import Control.Monad.IO.Class
( MonadIO, liftIO )
import Foreign
( Ptr )
import Foreign.C
( CFloat(..)
, CString
)
-- dear-imgui
import DearImGui.Context
( imguiContext )
-- import DearImGui.Enums
-- import DearImGui.Structs
-- inline-c
import qualified Language.C.Inline as C
-- inline-c-cpp
import qualified Language.C.Inline.Cpp as Cpp
C.context (Cpp.cppCtx <> C.bsCtx <> imguiContext)
C.include "imgui.h"
Cpp.using "namespace ImGui"
setIniFilename :: MonadIO m => CString -> m ()
setIniFilename ptr = liftIO do
[C.block|
void {
GetIO().IniFilename = $(char * ptr);
}
|]
setLogFilename :: MonadIO m => CString -> m ()
setLogFilename ptr = liftIO do
[C.block|
void {
GetIO().LogFilename = $(char * ptr);
}
|]
setMouseDoubleClickTime :: MonadIO m => CFloat -> m ()
setMouseDoubleClickTime seconds = liftIO do
[C.block|
void {
GetIO().MouseDoubleClickTime = $(float seconds);
}
|]
setMouseDoubleClickMaxDist :: MonadIO m => CFloat -> m ()
setMouseDoubleClickMaxDist pixels = liftIO do
[C.block|
void {
GetIO().MouseDoubleClickMaxDist = $(float pixels);
}
|]
setMouseDragThreshold :: MonadIO m => CFloat -> m ()
setMouseDragThreshold pixels = liftIO do
[C.block|
void {
GetIO().MouseDragThreshold = $(float pixels);
}
|]
setKeyRepeatDelay :: MonadIO m => CFloat -> m ()
setKeyRepeatDelay seconds = liftIO do
[C.block|
void {
GetIO().KeyRepeatDelay = $(float seconds);
}
|]
setKeyRepeatRate :: MonadIO m => CFloat -> m ()
setKeyRepeatRate pixels = liftIO do
[C.block|
void {
GetIO().KeyRepeatRate = $(float pixels);
}
|]
setUserData :: MonadIO m => Ptr () -> m ()
setUserData ptr = liftIO do
[C.block|
void {
GetIO().UserData = $(void* ptr);
}
|]
{- TODO:
bool WantCaptureMouse; // Set when Dear ImGui will use mouse inputs, in this case do not dispatch them to your main game/application (either way, always pass on mouse inputs to imgui). (e.g. unclicked mouse is hovering over an imgui window, widget is active, mouse was clicked over an imgui window, etc.).
bool WantCaptureKeyboard; // Set when Dear ImGui will use keyboard inputs, in this case do not dispatch them to your main game/application (either way, always pass keyboard inputs to imgui). (e.g. InputText active, or an imgui window is focused and navigation is enabled, etc.).
bool WantTextInput; // Mobile/console: when set, you may display an on-screen keyboard. This is set by Dear ImGui when it wants textual keyboard input to happen (e.g. when a InputText widget is active).
bool WantSetMousePos; // MousePos has been altered, backend should reposition mouse on next frame. Rarely used! Set only when ImGuiConfigFlags_NavEnableSetMousePos flag is enabled.
bool WantSaveIniSettings; // When manual .ini load/save is active (io.IniFilename == NULL), this will be set to notify your application that you can call SaveIniSettingsToMemory() and save yourself. Important: clear io.WantSaveIniSettings yourself after saving!
bool NavActive; // Keyboard/Gamepad navigation is currently allowed (will handle ImGuiKey_NavXXX events) = a window is focused and it doesn't use the ImGuiWindowFlags_NoNavInputs flag.
bool NavVisible; // Keyboard/Gamepad navigation is visible and allowed (will handle ImGuiKey_NavXXX events).
float Framerate; // Rough estimate of application framerate, in frame per second. Solely for convenience. Rolling average estimation based on io.DeltaTime over 120 frames.
int MetricsRenderVertices; // Vertices output during last call to Render()
int MetricsRenderIndices; // Indices output during last call to Render() = number of triangles * 3
int MetricsRenderWindows; // Number of visible windows
int MetricsActiveWindows; // Number of active windows
int MetricsActiveAllocations; // Number of active allocations, updated by MemAlloc/MemFree based on current context. May be off if you have multiple imgui contexts.
ImVec2 MouseDelta;
-}

View File

@ -0,0 +1,149 @@
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ViewPatterns #-}
{-| Helper: Manually clip large list of items.
If you are submitting lots of evenly spaced items and you have a random access to the list,
you can perform coarse clipping based on visibility to save yourself from processing those items at all.
The clipper calculates the range of visible items and advance the cursor to compensate for the non-visible items we have skipped.
Dear ImGui already clips items based on their bounds but it needs to measure text size to do so,
whereas manual coarse clipping before submission makes this cost and your own data fetching/submission cost almost null.
Usage:
@
clipper <- ListClipper.new
ListClipper.begin clipper 1000 -- We have 1000 elements, evenly spaced.
whileTrue (ListClipper.step clipper) $
start <- ListClipper.displayStart clipper
end <- ListClipper.displayEnd clipper
for_ [start .. end] \ix ->
ImGui.text $ "line number " <> show ix
@
Generally what happens is:
* Clipper lets you process the first element (DisplayStart = 0, DisplayEnd = 1) regardless of it being visible or not.
* User code submit one element.
* Clipper can measure the height of the first element
* Clipper calculate the actual range of elements to display based on the current clipping rectangle,
position the cursor before the first visible element.
* User code submit visible elements.
-}
module DearImGui.Raw.ListClipper
( ListClipper
, new
, delete
, begin
, displayStart
, displayEnd
, step
)
where
import Control.Monad.IO.Class
( MonadIO, liftIO )
import Foreign hiding (new)
import Foreign.C
import System.IO.Unsafe (unsafePerformIO)
-- dear-imgui
import DearImGui.Context
( imguiContext )
import DearImGui.Structs
( ImGuiListClipper )
-- inline-c
import qualified Language.C.Inline as C
-- inline-c-cpp
import qualified Language.C.Inline.Cpp as Cpp
C.context (Cpp.cppCtx <> C.bsCtx <> imguiContext)
C.include "imgui.h"
Cpp.using "namespace ImGui"
-- | @ImGuiListClipper@ object handle.
type ListClipper = Ptr ImGuiListClipper
-- | Create a new 'ListClipper' instance.
new :: (MonadIO m) => m ListClipper
new = liftIO do
[C.block|
ImGuiListClipper* {
return IM_NEW(ImGuiListClipper);
}
|]
-- | Destroy 'ListClipper' instance.
delete :: (MonadIO m) => ListClipper -> m ()
delete clipper = liftIO do
[C.block|
void {
IM_DELETE($(ImGuiListClipper* clipper));
}
|]
-- | ListClipper setup
--
-- @items_count@: Use 'maxBound' if you don't know how many items you have
-- (in which case the cursor won't be advanced in the final step).
--
-- @items_height@: Use -1.0f to be calculated automatically on first step.
-- Otherwise pass in the distance between your items, typically
-- 'getTextLineHeightWithSpacing' or 'getFrameHeightWithSpacing'.
--
-- Wraps @ListClipper::Begin()@.
begin :: (MonadIO m) => ListClipper -> CInt -> CFloat -> m ()
begin clipper items_count items_height = liftIO do
[C.block|
void {
$(ImGuiListClipper* clipper)->Begin($(int items_count), $(float items_height));
}
|]
-- | An accessor for @ListClipper::Begin@
displayStart :: ListClipper -> CInt
displayStart clipper = unsafePerformIO do
[C.exp|
int {
$(ImGuiListClipper* clipper)->DisplayStart
}
|]
-- | An accessor for @ListClipper::DisplayStart@
displayEnd :: ListClipper -> CInt
displayEnd clipper = unsafePerformIO
[C.exp|
int {
$(ImGuiListClipper* clipper)->DisplayEnd
}
|]
-- | Call until it returns 'False'.
--
-- The 'displayStart'/'displayEnd' fields will be set and you can process/draw those items.
--
-- Wraps @ListClipper::Step()@.
step :: (MonadIO m) => ListClipper -> m Bool
step clipper = liftIO do
(0 /=) <$> [C.block|
bool {
return $(ImGuiListClipper* clipper)->Step();
}
|]

View File

@ -4,6 +4,8 @@
module DearImGui.Structs where
-- base
import Data.Word
( Word32, Word16 )
import Foreign
( Storable(..), castPtr, plusPtr )
@ -77,3 +79,22 @@ data ImGuiContext
-- | Individual font handle.
data ImFont
-- | Font configuration handle.
data ImFontConfig
-- | Glyph ranges builder handle.
data ImFontGlyphRangesBuilder
-- | Opaque DrawList handle.
data ImDrawList
-- | 'DearImGui.Raw.ListClipper.ListClipper' pointer tag.
data ImGuiListClipper
-- | 32-bit unsigned integer (often used to store packed colors).
type ImU32 = Word32
-- | Single wide character (used mostly in glyph management)
type ImWchar = Word16
-- FIXME: consider IMGUI_USE_WCHAR32