Skip to content

Commit bb94341

Browse files
authored
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
1 parent 13e6824 commit bb94341

File tree

12 files changed

+1380
-95
lines changed

12 files changed

+1380
-95
lines changed

dear-imgui.cabal

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,12 @@ library
124124
src
125125
exposed-modules:
126126
DearImGui
127+
DearImGui.FontAtlas
127128
DearImGui.Raw
128129
DearImGui.Raw.DrawList
130+
DearImGui.Raw.Font
131+
DearImGui.Raw.Font.Config
132+
DearImGui.Raw.Font.GlyphRanges
129133
DearImGui.Raw.ListClipper
130134
DearImGui.Raw.IO
131135
other-modules:
@@ -295,6 +299,14 @@ executable readme
295299
if (!flag(examples) || !flag(sdl) || !flag(opengl2))
296300
buildable: False
297301

302+
executable fonts
303+
import: common, build-flags
304+
main-is: Main.hs
305+
hs-source-dirs: examples/fonts
306+
build-depends: sdl2, gl, dear-imgui, managed
307+
if (!flag(examples) || !flag(sdl) || !flag(opengl2))
308+
buildable: False
309+
298310
executable image
299311
import: common, build-flags
300312
main-is: Image.hs

examples/fonts/Main.hs

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
{-# language BlockArguments #-}
2+
{-# language LambdaCase #-}
3+
{-# language OverloadedStrings #-}
4+
{-# language RecordWildCards #-}
5+
{-# language NamedFieldPuns #-}
6+
{-# language DeriveTraversable #-}
7+
8+
{- | Font usage example.
9+
10+
Loads two non-standard fonts
11+
12+
This example uses NotoSansJP-Regular.otf from Google Fonts
13+
Licensed under the SIL Open Font License, Version 1.1
14+
https://fonts.google.com/noto/specimen/Noto+Sans+JP
15+
-}
16+
17+
module Main ( main ) where
18+
19+
import Control.Exception
20+
import Control.Monad
21+
import Control.Monad.IO.Class
22+
import Control.Monad.Managed
23+
import Data.IORef
24+
import DearImGui
25+
import qualified DearImGui.FontAtlas as FontAtlas
26+
import DearImGui.OpenGL2
27+
import DearImGui.SDL
28+
import DearImGui.SDL.OpenGL
29+
import Graphics.GL
30+
import SDL
31+
32+
-- Rebuild syntax enables us to keep fonts in any
33+
-- traversable type, so let's make our life a little easier.
34+
-- But feel free to use lists or maps.
35+
data FontSet a = FontSet
36+
{ droidFont :: a
37+
, defaultFont :: a
38+
, notoFont :: a
39+
}
40+
deriving (Functor, Foldable, Traversable)
41+
42+
main :: IO ()
43+
main = do
44+
-- Window initialization is similar to another examples.
45+
initializeAll
46+
runManaged do
47+
window <- do
48+
let title = "Hello, Dear ImGui!"
49+
let config = defaultWindow { windowGraphicsContext = OpenGLContext defaultOpenGL }
50+
managed $ bracket (createWindow title config) destroyWindow
51+
glContext <- managed $ bracket (glCreateContext window) glDeleteContext
52+
_ <- managed $ bracket createContext destroyContext
53+
_ <- managed_ $ bracket_ (sdl2InitForOpenGL window glContext) sdl2Shutdown
54+
_ <- managed_ $ bracket_ openGL2Init openGL2Shutdown
55+
56+
-- We use high-level syntax to build font atlas and
57+
-- get handles to use in the main loop.
58+
fontSet <- FontAtlas.rebuild FontSet
59+
{ -- The first mentioned font is loaded first
60+
-- and set as a global default.
61+
droidFont =
62+
FontAtlas.FromTTF
63+
"./imgui/misc/fonts/DroidSans.ttf"
64+
15
65+
Nothing
66+
FontAtlas.Cyrillic
67+
68+
-- You also may use a default hardcoded font for
69+
-- some purposes (i.e. as fallback)
70+
, defaultFont =
71+
FontAtlas.DefaultFont
72+
73+
-- To optimize atlas size, use ranges builder and
74+
-- provide source localization data.
75+
, notoFont =
76+
FontAtlas.FromTTF
77+
"./examples/fonts/NotoSansJP-Regular.otf"
78+
20
79+
Nothing
80+
( FontAtlas.RangesBuilder $ mconcat
81+
[ FontAtlas.addRanges FontAtlas.Latin
82+
, FontAtlas.addText "私をクリックしてください"
83+
, FontAtlas.addText "こんにちは"
84+
]
85+
)
86+
}
87+
88+
liftIO $ do
89+
fontFlag <- newIORef False
90+
mainLoop window do
91+
let FontSet{..} = fontSet
92+
withWindowOpen "Hello, ImGui!" do
93+
-- To use a font for widget text, you may either put it
94+
-- into a 'withFont' block:
95+
withFont defaultFont do
96+
text "Hello, ImGui!"
97+
98+
text "Привет, ImGui!"
99+
100+
-- ...or you can explicitly push and pop a font.
101+
-- Though it's not recommended.
102+
toggled <- readIORef fontFlag
103+
104+
when toggled $
105+
pushFont notoFont
106+
107+
-- Some of those are only present in Noto font range
108+
-- and will render as `?`s.
109+
text "こんにちは, ImGui!"
110+
111+
let buttonText = if toggled then "私をクリックしてください" else "Click Me!"
112+
button buttonText >>= \clicked ->
113+
when clicked $
114+
modifyIORef' fontFlag not
115+
116+
when toggled
117+
popFont
118+
119+
showDemoWindow
120+
121+
mainLoop :: Window -> IO () -> IO ()
122+
mainLoop window frameAction = loop
123+
where
124+
loop = unlessQuit do
125+
openGL2NewFrame
126+
sdl2NewFrame
127+
newFrame
128+
129+
frameAction
130+
131+
glClear GL_COLOR_BUFFER_BIT
132+
render
133+
openGL2RenderDrawData =<< getDrawData
134+
glSwapWindow window
135+
136+
loop
137+
138+
unlessQuit action = do
139+
shouldQuit <- checkEvents
140+
if shouldQuit then pure () else action
141+
142+
checkEvents = do
143+
pollEventWithImGui >>= \case
144+
Nothing ->
145+
return False
146+
Just event ->
147+
(isQuit event ||) <$> checkEvents
148+
149+
isQuit event =
150+
SDL.eventPayload event == SDL.QuitEvent

examples/fonts/NotoSansJP-Regular.otf

4.34 MB
Binary file not shown.

examples/vulkan/Main.hs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -134,12 +134,6 @@ app = do
134134
ImGui.createContext
135135
ImGui.destroyContext
136136

137-
logDebug "Adding fonts"
138-
ImGui.clearFontAtlas
139-
_default <- ImGui.addFontDefault
140-
_custom <- ImGui.addFontFromFileTTF "imgui/misc/fonts/ProggyTiny.ttf" 10
141-
ImGui.buildFontAtlas
142-
143137
let
144138
preferredFormat :: Vulkan.SurfaceFormatKHR
145139
preferredFormat =

src/DearImGui.hs

Lines changed: 11 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,11 @@ module DearImGui
8686
, pushStyleVar
8787
, popStyleVar
8888

89+
, withFont
90+
, Raw.Font.pushFont
91+
, Raw.Font.popFont
92+
, Raw.Font.Font
93+
8994
-- * Cursor/Layout
9095
, Raw.separator
9196
, Raw.sameLine
@@ -247,13 +252,6 @@ module DearImGui
247252
, Raw.wantCaptureMouse
248253
, Raw.wantCaptureKeyboard
249254

250-
-- * Fonts
251-
, Raw.Font
252-
, addFontFromFileTTF
253-
, Raw.addFontDefault
254-
, Raw.buildFontAtlas
255-
, Raw.clearFontAtlas
256-
257255
-- * Utilities
258256

259257
-- ** ListClipper
@@ -287,6 +285,9 @@ import System.IO
287285
-- dear-imgui
288286
import DearImGui.Enums
289287
import DearImGui.Structs
288+
import qualified DearImGui.Raw as Raw
289+
import qualified DearImGui.Raw.Font as Raw.Font
290+
import qualified DearImGui.Raw.ListClipper as Raw.ListClipper
290291

291292
-- managed
292293
import qualified Control.Monad.Managed as Managed
@@ -303,9 +304,6 @@ import Control.Monad.IO.Class
303304
import UnliftIO (MonadUnliftIO)
304305
import UnliftIO.Exception (bracket, bracket_)
305306

306-
import qualified DearImGui.Raw as Raw
307-
import qualified DearImGui.Raw.ListClipper as Raw.ListClipper
308-
309307
-- vector
310308
import qualified Data.Vector as V
311309
import qualified Data.Vector.Storable as VS
@@ -1691,24 +1689,9 @@ popStyleVar :: (MonadIO m) => Int -> m ()
16911689
popStyleVar n = liftIO do
16921690
Raw.popStyleVar (fromIntegral n)
16931691

1694-
1695-
-- | Load a font from TTF file.
1696-
--
1697-
-- Specify font path and atlas glyph size.
1698-
--
1699-
-- Use 'addFontDefault' if you want to retain built-in font too.
1700-
--
1701-
-- Call 'buildFontAtlas' after adding all the fonts.
1702-
--
1703-
-- Call backend-specific `CreateFontsTexture` before using 'newFrame'.
1704-
addFontFromFileTTF :: MonadIO m => FilePath -> Float -> m (Maybe Raw.Font)
1705-
addFontFromFileTTF font size = liftIO do
1706-
res@(Raw.Font ptr) <- withCString font \fontPtr ->
1707-
Raw.addFontFromFileTTF fontPtr (CFloat size)
1708-
pure $
1709-
if castPtr ptr == nullPtr
1710-
then Nothing
1711-
else Just res
1692+
-- | Render widgets inside the block using provided font.
1693+
withFont :: MonadUnliftIO m => Raw.Font.Font -> m a -> m a
1694+
withFont font = bracket_ (Raw.Font.pushFont font) Raw.Font.popFont
17121695

17131696
-- | Clips a large list of items
17141697
--

src/DearImGui/Context.hs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,12 @@ imguiContext = mempty
3434
, ( TypeName "ImVec3", [t| ImVec3 |] )
3535
, ( TypeName "ImVec4", [t| ImVec4 |] )
3636
, ( TypeName "ImU32", [t| ImU32 |] )
37+
, ( TypeName "ImWchar", [t| ImWchar |] )
3738
, ( TypeName "ImDrawList", [t| ImDrawList |] )
3839
, ( TypeName "ImGuiContext", [t| ImGuiContext |] )
3940
, ( TypeName "ImFont", [t| ImFont |] )
41+
, ( TypeName "ImFontConfig", [t| ImFontConfig |] )
42+
, ( TypeName "ImFontGlyphRangesBuilder", [t| ImFontGlyphRangesBuilder |] )
4043
, ( TypeName "ImGuiListClipper", [t| ImGuiListClipper |] )
4144
]
4245
}

0 commit comments

Comments
 (0)