Safe Haskell | None |
---|---|
Language | Haskell2010 |
- module Graphics.UI.FLTK.LowLevel.Fl_Types
- module Graphics.UI.FLTK.LowLevel.Adjuster
- module Graphics.UI.FLTK.LowLevel.Bitmap
- module Graphics.UI.FLTK.LowLevel.Box
- module Graphics.UI.FLTK.LowLevel.Browser
- module Graphics.UI.FLTK.LowLevel.Button
- module Graphics.UI.FLTK.LowLevel.CheckButton
- module Graphics.UI.FLTK.LowLevel.Choice
- module Graphics.UI.FLTK.LowLevel.Clock
- module Graphics.UI.FLTK.LowLevel.CopySurface
- module Graphics.UI.FLTK.LowLevel.Counter
- module Graphics.UI.FLTK.LowLevel.Dial
- module Graphics.UI.FLTK.LowLevel.DoubleWindow
- module Graphics.UI.FLTK.LowLevel.Draw
- module Graphics.UI.FLTK.LowLevel.FillDial
- module Graphics.UI.FLTK.LowLevel.FillSlider
- module Graphics.UI.FLTK.LowLevel.Group
- module Graphics.UI.FLTK.LowLevel.HorFillSlider
- module Graphics.UI.FLTK.LowLevel.HorNiceSlider
- module Graphics.UI.FLTK.LowLevel.HorSlider
- module Graphics.UI.FLTK.LowLevel.HorValueSlider
- module Graphics.UI.FLTK.LowLevel.Image
- module Graphics.UI.FLTK.LowLevel.ImageSurface
- module Graphics.UI.FLTK.LowLevel.Input
- module Graphics.UI.FLTK.LowLevel.Output
- module Graphics.UI.FLTK.LowLevel.IntInput
- module Graphics.UI.FLTK.LowLevel.FileInput
- module Graphics.UI.FLTK.LowLevel.LightButton
- module Graphics.UI.FLTK.LowLevel.LineDial
- module Graphics.UI.FLTK.LowLevel.MenuPrim
- module Graphics.UI.FLTK.LowLevel.MenuButton
- module Graphics.UI.FLTK.LowLevel.MenuItem
- module Graphics.UI.FLTK.LowLevel.NiceSlider
- module Graphics.UI.FLTK.LowLevel.OverlayWindow
- module Graphics.UI.FLTK.LowLevel.Pixmap
- module Graphics.UI.FLTK.LowLevel.Positioner
- module Graphics.UI.FLTK.LowLevel.Progress
- module Graphics.UI.FLTK.LowLevel.RadioLightButton
- module Graphics.UI.FLTK.LowLevel.RepeatButton
- module Graphics.UI.FLTK.LowLevel.ReturnButton
- module Graphics.UI.FLTK.LowLevel.Roller
- module Graphics.UI.FLTK.LowLevel.RoundButton
- module Graphics.UI.FLTK.LowLevel.Scrollbar
- module Graphics.UI.FLTK.LowLevel.SelectBrowser
- module Graphics.UI.FLTK.LowLevel.SimpleCounter
- module Graphics.UI.FLTK.LowLevel.SingleWindow
- module Graphics.UI.FLTK.LowLevel.Slider
- module Graphics.UI.FLTK.LowLevel.Spinner
- module Graphics.UI.FLTK.LowLevel.MenuBar
- module Graphics.UI.FLTK.LowLevel.SysMenuBar
- module Graphics.UI.FLTK.LowLevel.Tabs
- module Graphics.UI.FLTK.LowLevel.Table
- module Graphics.UI.FLTK.LowLevel.TableRow
- module Graphics.UI.FLTK.LowLevel.ToggleButton
- module Graphics.UI.FLTK.LowLevel.Tree
- module Graphics.UI.FLTK.LowLevel.TreeItem
- module Graphics.UI.FLTK.LowLevel.TreePrefs
- module Graphics.UI.FLTK.LowLevel.Valuator
- module Graphics.UI.FLTK.LowLevel.ValueInput
- module Graphics.UI.FLTK.LowLevel.ValueOutput
- module Graphics.UI.FLTK.LowLevel.ValueSlider
- module Graphics.UI.FLTK.LowLevel.Widget
- module Graphics.UI.FLTK.LowLevel.Window
- module Graphics.UI.FLTK.LowLevel.Wizard
- module Graphics.UI.FLTK.LowLevel.TextSelection
- module Graphics.UI.FLTK.LowLevel.TextBuffer
- module Graphics.UI.FLTK.LowLevel.TextDisplay
- module Graphics.UI.FLTK.LowLevel.TextEditor
- module Graphics.UI.FLTK.LowLevel.NativeFileChooser
- module Graphics.UI.FLTK.LowLevel.Tile
- module Graphics.UI.FLTK.LowLevel.Pack
- module Graphics.UI.FLTK.LowLevel.Scrolled
- module Graphics.UI.FLTK.LowLevel.Ask
- module Graphics.UI.FLTK.LowLevel.ColorChooser
- module Graphics.UI.FLTK.LowLevel.FileBrowser
- module Graphics.UI.FLTK.LowLevel.JPEGImage
- module Graphics.UI.FLTK.LowLevel.RGBImage
- module Graphics.UI.FLTK.LowLevel.BMPImage
- module Graphics.UI.FLTK.LowLevel.GIFImage
- module Graphics.UI.FLTK.LowLevel.XBMImage
- module Graphics.UI.FLTK.LowLevel.XPMImage
- module Graphics.UI.FLTK.LowLevel.PNGImage
- module Graphics.UI.FLTK.LowLevel.PNMImage
- module Graphics.UI.FLTK.LowLevel.Dispatch
- module Graphics.UI.FLTK.LowLevel.Hierarchy
Motivation
This library aims to make it easy for users to build native apps that work portably across platforms.
I'm also very interested the user interface renaissance in the programming community, whether the various kinds of functional reactive programming, meta-object protocol UI's, or something like React.js.
The hope is that a low-cost hassle-free way of getting a UI up and running without having to deal with browser, authentication and compilation issues will make it more fun to play around with these great ideas using Haskell.
Why a native toolkit?
Even in this era of web interfaces it is still useful to be able to make native apps. They are usually faster and have fewer security issues.
Why FLTK?
- I chose FLTK because it was small enough that one person could bind the whole thing in an initial pass. Larger toolkits like QT, although way slicker, would require many man-years of effort.
- FLTK is quite featureful
- FLTK is mature and maintained, the project is about 20 years old and I have had good experiences with the community
- FLTK comes with a simple but quite useful GUI builder, Fluid which is now able to generate Haskell code. See the `Fluid Support` section for more details.
What about HsQML/WxHaskell/Gtk2Hs?
These are all great projects and produce really nice UI's, but they all fail at least one of criterion listed under the Goals section below.
To my knowledge, as of the second quarter of 2017, no other package in the Haskell ecosystem meets all those constraints.
Goals
The goals of this library are to provide a low level API to the FLTK that:
- provides full coverage of the toolkit allowing the user to write GUI's in pure Haskell.
- feels like it has polymorphic dispatch, meaning a single function dispatches to the right implementation based on the type of widget it is given
- is not monolithic, meaning new widgets can be incorporated the user's application without needing to recompile this library
- is easy to install. This library has a minimum of dependencies and FLTK itself compiles cleanly on most architectures. And now there is even a bundled option where Cabal/Stack build FLTK for you behind the scenes.
- allows the user to produce statically linked binaries with a minimum of external dependencies
- includes a lot of complete working demos so that you can get up and running faster.
- comes with GUI builder support to alleviate the tedium of laying out widgets by hand
Obstacles
This section attempts to briefly highlight some possible dealbreakers users might want to know about before proceeding. To be clear, building and deploying portable static application binaries works well on all platforms which is why the library is considered usable. And most of these issues are being aggressively addressed but in the interests of full disclosure ...
Look & Feel
The default look FLTK apps can be charitably described as retro. And FLTK has no support for theming since it was designed for the embedded domain and trades off slickness for speed, portability and binary size.
But that doesn't mean the user is limited to the default! FLTK is quite flexible and it is pretty easy to change the way a widget looks and behaves but there is no "theme file" that applies uniformly to all widgets in the app.
Compile Times
Currently a dense app with ~ 160-180 widgets crammed into the same window takes 9-12 seconds to compile with GHC 7.10.3 on a 32GB quad-core machine. The good news is that this is a known issue.
Installation
There are two ways to install FLTKHS, building with the bundled FLTK GUI library, or compiling and installing FLTK from scratch yourself. The bundled way is by far the easiest on all platforms. It is completely self-contained, you don't need any sudo access to your system.
Build With Bundled FLTK
Linux & *BSD
The steps are:
- Make sure to have OpenGL installed if you need it.
- Ensure that
make
,autoconf
andautoheader
are available on your system - Download & install Stack
- Download & install the FLTKHS hello world skeleton
- Verify the install by running `fltkhs-hello-world`
Download & Install Stack
Pick the Stack installer that matches your distribution and install according the instructions.
Download & Install the FLTKHS Hello World Skeleton
Downloading Without Git
If git
is not installed download the latest version of the fltkhs-hello-world application skeleton from here
Extract and rename the archive:
> tar -zxvf fltkhs-hello-world-master.tar.gz > mv fltkhs-hello-world-master fltkhs-hello-world
Downloading With Git
If git
is available:
> git clone http://github.com/deech/fltkhs-hello-world
Building
Build it with Stack:
> cd fltkhs-hello-world > stack setup > stack install --flag fltkhs:bundled or if you need OpenGL support > stack install --flag fltkhs:bundled --flag fltkhs:opengl
Verify The Install
Test that the build completed successfully by invoking incredibly unimpressive
> stack exec fltkhs-hello-world
You will be greeted by an incredibly boring little window with a button that says "Hello world", if you click it, it will change to "Goodbye world."
Mac (Yosemite, El Capitan, Sierra)
Mac versions older than El Capitan and Yosemite are not supported.
The general steps are:
- Brew Install Stack
- Download & install the FLTKHS hello world skeleton
- Verify the install by running `fltkhs-hello-world`
Brew Install Stack
This should be as simple as:
> brew install haskell-stack
Brew Install Autoconf
> brew install autoconf
Download & Install the FLTKHS Hello World Skeleton
Downloading Without Git
If git
is not installed download the latest version of the fltkhs-hello-world application skeleton from here
Extract the archive:
> cd /Users/<username>/Downloads/ > tar -zxvf fltkhs-hello-world-master.tar.gz > mv fltkhs-hello-world-master fltkhs-hello-world
Downloading With Git
If git
is available:
> git clone http://github.com/deech/fltkhs-hello-world
Building
Build it with Stack:
> cd fltkhs-hello-world > stack setup > stack install --flag fltkhs:bundled or if you need OpenGL support > stack install --flag fltkhs:bundled --flag fltkhs:opengl
Verify The Install
Test that the build completed successfully by invoking incredibly unimpressive
> stack exec fltkhs-hello-world
You will be greeted by an incredibly boring little window with a button that says "Hello world", if you click it, it will change to "Goodbye world."
Windows(7,8,10)(64-bit)
This install guide has been tested on a Windows 7, 8 and 10.
Install Stack
Downloading and following the default instructions for the standard Windows installer should be enough.
If the install succeeded stack
should on the PATH. To test run 'cmd.exe' and do:
> stack --version
Now downloading and setup the latest GHC via stack
:
> stack setup
From this point on we can live in the MSYS2 shell that comes with Stack. It is a far superior environment to the command prompt. To open the MSYS2 shell do:
> stack exec mintty
Install Necessary Utilities via Pacman
In the MSYS2 shell prompt update and upgrade the MSYS2 installation:
> pacman -Syy > pacman -Syu
... install packages for download and extracting packages:
> pacman -S wget > pacman -S tar > pacman -S unzip > pacman -S zip > pacman -S man
... and building C/C++ programs:
> pacman -S autoconf > pacman -S make > pacman -S automake
Download And Install The FLTKHS Hello World Skeleton
The fltkhs-hello-world skeleton is a simple Hello World GUI which provides the base structure for FLTKHS applications. Please see the Demos
section of this document for examples of apps that show off more complex uses of the API.
> wget --no-check-certificate https://github.com/deech/fltkhs-hello-world/archive/master.zip > unzip master.zip > mv fltkhs-hello-world-master fltkhs-hello-world > cd fltkhs-hello-world
And install with:
> stack install --flag fltkhs:bundled or if you need OpenGL support > stack install --flag fltkhs:bundled --flag fltkhs:opengl
To test the installation:
> stack exec fltkhs-hello-world
You will be greeted by an incredibly boring little window with a button that says "Hello world", if you click it, it will change to "Goodbye world."
Packaging A Windows Executable
While the 'fltkhs-hello-world' application is mostly stand-alone the MSYS2 environment bundled with stack
seems to require 3 runtime DLLs. The DLLs are bundled with stack
so it's easy to zip them up with the executable and deploy. The required DLLs are: 'libstdc++-6.dll', 'libgcc_s_seh-1.dll' and 'libwinpthread-1.dll'.
First create the directory that will contain the executable and DLLs:
> mkdir /tmp/fltkhs-hello-world
Copy the executable over to that directory:
> cp `which fltkhs-hello-world` /tmp/fltkhs-hello-world
Copy over the DLLs. They are usually located in '../<ghc-version>/mingw/bin' but to make the process slightly less fragile we specify the directory relative to whatever ghc
is currently in stack
's context:
> cp `dirname $(which ghc)`../mingw/bin/libstdc++-6.dll /tmp/fltkhs-hello-world > cp `dirname $(which ghc)`../mingw/bin/libgcc_s_seh-1.dll /tmp/fltkhs-hello-world > cp `dirname $(which ghc)`../mingw/bin/libwinpthread-1.dll /tmp/fltkhs-hello-world
Zip up archive:
> cd /tmp > zip fltkhs-hello-world.zip fltkhs-hello-world/*
And that's it! Any Windows 10 user should now be able to extract 'fltkhs-hello-world.zip' and run 'fltkhs-hello-world.exe'.
Compile FLTK Yourself
Linux & *BSD
The steps are:
- Make sure to have OpenGL installed if you need it
- Download & install Stack
- Download & install FLTK 1.3.4-1
- Download & install the FLTKHS hello world skeleton
- Verify the install by running `fltkhs-hello-world`
Download & Install Stack
Pick the Stack installer that matches your distribution and install according the instructions.
Download & Install FLTK-1.3.4-1
Please make sure to only download version FLTK 1.3.4-1. It should build and install smoothly with the standard:
> ./configure --enable-shared --enable-localjpeg --enable-localzlib --enable-localpng or if you need OpenGL support > ./configure --enable-gl --enable-shared --enable-localjpeg --enable-localzlib --enable-localpng > make > sudo make install
If you didn't install FLTK from source you can use the 'fltk-config' tool to ensure that version 1.3.4-1 is installed:
> fltk-config --version 1.3.4-1
Additionally the FLTK headers should be in the include path and, along with
the standard FLTK libraries, fltk_images
, and fltk_gl
. You will also need
the make
, autoconf
, and autoheader
tools to build the Haskell bindings.
The reason we install from source is that some package managers seem to be behind on versions (as of this writing Ubuntu 14.04 is still on 1.3.2) and others put the headers and libraries in a non-standard locations which will cause the Haskell bindings to throw compilation errors.
Download & Install the FLTKHS Hello World Skeleton
Downloading Without Git
If git
is not installed download the latest version of the fltkhs-hello-world application skeleton from here
Extract and enter the archive:
> tar -zxvf fltkhs-hello-world-master.tar.gz > mv fltkhs-hello-world-master fltkhs-hello-world
Downloading With Git
If git
is available:
> git clone http://github.com/deech/fltkhs-hello-world
Building
Build it with Stack:
> cd fltkhs-hello-world > stack setup > stack install or if you need OpenGL support > stack install --flag fltkhs:opengl
Note: If the install
step produces a flood of `undefined reference` errors
please ensure that you have the right version of FLTK (1.3.4-1) installed and
that the headers are in the expected locations. It seems as though some
package managers put the libraries and headers in non-standard places so it
is best to build from source.
Verify The Install
Test that the build completed successfully by invoking incredibly unimpressive
> stack exec fltkhs-hello-world
You will be greeted by an incredibly boring little window with a button that says "Hello world", if you click it, it will change to "Goodbye world."
Mac (Yosemite & El Capitan)
Unfortunately Mac version older than El Capitan and Yosemite are not supported.
The general steps are:
- Brew Install Stack
- Download & install the FLTKHS hello world skeleton
- Verify the install by running `fltkhs-hello-world`
Brew Install Stack
This should be as simple as:
> brew install haskell-stack
Brew Install Autoconf
> brew install autoconf
Compile & Install FLTK from Source.
The brew
package for the current stable release of FLTK is broken. Fortunately installing from source is pretty
quick and painless.
> wget http://fltk.org/pub/fltk/1.3.4/fltk-1.3.4-1-source.tar.gz > tar -zxf fltk-1.3.4-1-source.tar.gz > cd fltk-1.3.4-1 > ./configure --enable-shared --enable-localjpeg --enable-localzlib --enable-localpng or if you need OpenGL support > ./configure --enable-gl --enable-shared --enable-localjpeg --enable-localzlib --enable-localpng > make > sudo make install > fltk-config --version 1.3.4-1
Download & Install the FLTKHS Hello World Skeleton
Downloading Without Git
If git
is not installed download the latest version of the fltkhs-hello-world application skeleton from here
Extract the archive:
> cd /Users/<username>/Downloads/ > tar -zxvf fltkhs-hello-world-master.tar.gz > mv fltkhs-hello-world-master fltkhs-hello-world
Downloading With Git
If git
is available:
> git clone http://github.com/deech/fltkhs-hello-world
Building
Build it with Stack:
> cd fltkhs-hello-world > stack setup > stack install or if you need OpenGL support > stack install --flag fltkhs:opengl
Verify The Install
Test that the build completed successfully by invoking incredibly unimpressive
> stack exec fltkhs-hello-world
You will be greeted by an incredibly boring little window with a button that says "Hello world", if you click it, it will change to "Goodbye world."
Windows(7,8,10)(64-bit)
This install guide has been tested on a Windows 7, 8 and 10.
Install Stack
Downloading and following the default instructions for the standard Windows installer should be enough.
If the install succeeded stack
should on the PATH. To test run 'cmd.exe' and do:
> stack --version
Now downloading and setup the latest GHC via stack
:
> stack setup
From this point on we can live in the MSYS2 shell that comes with Stack. It is a far superior environment to the command prompt. To open the MSYS2 shell do:
> stack exec mintty
Install Necessary Utilities via Pacman
In the MSYS2 shell prompt update and upgrade the MSYS2 installation:
> pacman -Syy > pacmay -Syu
... install packages for download and extracting packages:
> pacman -S wget > pacman -S tar > pacman -S unzip > pacman -S zip > pacman -S man
... and building C/C++ programs:
> pacman -S autoconf > pacman -S make > pacman -S automake
Download and Install FLTK
Download the latest stable build of FLTK:
> wget --no-check-certificate http://fltk.org/pub/fltk/1.3.4/fltk-1.3.4-1-source.tar.gz
Untar the FLTK archive and enter the directory:
> tar -zxf fltk-1.3.4-1-source.tar.gz > cd fltk-1.3.4-1
Configure, make and install:
> ./configure --enable-shared --enable-localjpeg --enable-localzlib --enable-localpng or if you need OpenGL support > ./configure --enable-gl --enable-shared --enable-localjpeg --enable-localzlib --enable-localpng > make > make install
You can test your installation by running:
> fltk-config 1.3.4-1
Download And Install The FLTKHS Hello World Skeleton
The fltkhs-hello-world skeleton is a simple Hello World GUI which provides the base structure for FLTKHS applications. Please see the Demos
section of this document for examples of apps that show off more complex uses of the API.
> wget --no-check-certificate https://github.com/deech/fltkhs-hello-world/archive/master.zip > unzip master.zip > mv fltkhs-hello-world-master fltkhs-hello-world > cd fltkhs-hello-world
And install with:
> stack install or if you need OpenGL support > stack install --flag fltkhs:opengl
To test the installation:
> stack exec fltkhs-hello-world
You will be greeted by an incredibly boring little window with a button that says "Hello world", if you click it, it will change to "Goodbye world."
Packaging A Windows Executable
While the 'fltkhs-hello-world' application is mostly stand-alone the MSYS2 environment bundled with stack
seems to require 3 runtime DLLs. The DLLs are bundled with stack
so it's easy to zip them up with the executable and deploy. The required DLLs are: 'libstdc++-6.dll', 'libgcc_s_seh-1.dll' and 'libwinpthread-1.dll'.
First create the directory that will contain the executable and DLLs:
> mkdir /tmp/fltkhs-hello-world
Copy the executable over to that directory:
> cp `which fltkhs-hello-world` /tmp/fltkhs-hello-world
Copy over the DLLs. They are usually located in '../<ghc-version>/mingw/bin' but to make the process slightly less fragile we specify the directory relative to whatever ghc
is currently in stack
's context:
> cp `dirname $(which ghc)`../mingw/bin/libstdc++-6.dll /tmp/fltkhs-hello-world > cp `dirname $(which ghc)`../mingw/bin/libgcc_s_seh-1.dll /tmp/fltkhs-hello-world > cp `dirname $(which ghc)`../mingw/bin/libwinpthread-1.dll /tmp/fltkhs-hello-world
Zip up archive:
> cd /tmp > zip fltkhs-hello-world.zip fltkhs-hello-world/*
And that's it! Any Windows 10 user should now be able to extract 'fltkhs-hello-world.zip' and run 'fltkhs-hello-world.exe'.
Demos
FLTKHS has a almost 25 end-to-end demo applications to help the user get started. They are split into two sets, those written manually and those that show how to use FLUID.
The READMEs in the repos have installation instructions but they assume that the user has successfully installed FLTK and the 'fltkhs-hello-world' app (see platform specific instructions above).
Getting Started
By this point, I assume that you have successfully installed hello world (see above) or one of the demo packages.
Quick Start
The quickest way to get started is to look at the source for the FLTKHS project skeleton. Though it is a simple app it shows the basics of widget creation and callbacks.
Other demo packages show more complicated usage of the API.
Since the API is a low-level binding, code using it takes on the imperative style of the underlying toolkit. Fortunately it should look pretty familiar to those who have used object-oriented GUI toolkits before.
Fluid Support
This package also comes with a utility (fltkhs-fluidtohs) that takes a user interface generated using the Fluid GUI builder that ships with FLTK and generates Haskell code.
Now the user can drag and drop widgets into place instead of having to calculate coordinates and sizes by hand. Additionally arbitrary Haskell code can be inserted into Fluid interfaces allowing user to do most of callback wiring directly from Fluid.
The quickest way to get started is to download the
Fluid/Haskell project template.
The Setup.hs
that comes with the skeleton is configured to use
the 'fltkhs-fluidtohs' utility to automatically convert any Fluid in src
directory into a Haskell module of the same name during the preprocess step.
This means using Fluid in a FLTKHS project is as simple as creating a Fluid
interface and running 'stack build --flag fltkhs:bundled' or 'stack install --flag fltkhs:bundled'.
Additionally the fltkhs-fluid-demos package comes with a number of demos that show off how Fluid integrates with FLTKS.
Stack Traces
In a traditional callback-heavy API such as FLTKHS null pointers happen which
is why FLTKHS supports partial stack traces. All FLTK functions throw an
error along with a stack trace when given a null Ref
.
For pre-7.10 GHCs stack traces will only be shown if the 'xc' flag is used when compiling FLTKHS.
If compiled with GHC > 7.10 a partial stack trace is transparently available to the user. The recently minted 'CallStack' implicit parameter is used to get a trace of the function that made the offending call along with a file name and line number. So, for example, in the following code:
buttonCb :: Ref Button -> IO () buttonCb b' = do FL.deleteWidget b' l' <- getLabel b' ... main :: IO () main = do window <- windowNew ... begin window b' <- buttonNew ... setCallback b' buttonCb ...
a button is placed inside a window in the main method but the first time it is clicked the callback will delete it and then try
the extract the label from the null Ref
.
The resulting stack trace will look something like:
Ref does not exist. ?loc, called at src/Graphics/UI/FLTK/LowLevel/Fl_Types.chs:395:58 in fltkh_Cx8029B5VOwKjdT0OwMERC:Graphics.UI.FLTK.LowLevel.Fl_Types toRefPtr, called at src/Graphics/UI/FLTK/LowLevel/Fl_Types.chs:403:22 in fltkh_Cx8029B5VOwKjdT0OwMERC:Graphics.UI.FLTK.LowLevel.Fl_Types withRef, called at src/Graphics/UI/FLTK/LowLevel/Hierarchy.hs:1652:166 in fltkh_Cx8029B5VOwKjdT0OwMERC:Graphics.UI.FLTK.LowLevel.Hierarchy getLabel, called at src/Main.hs:11:10 in main:Main
It says that the null pointer was originally detected in the library function toRefPtr
function which was called by the library function withRef
, which
was called by getLabel
on line 11 of 'src/Main.hs'. Notice, however, that the trace stops there. It does not tell you getLabel
was invoked from buttonCb
.
For a more detailed trace the CallStack
implicit parameter needs to be passed to each function in the chain like:
buttonCb :: (?loc :: CallStack) => Ref Button ... ... main :: IO () ...
API Guide
Guide to the Haddock Docs
Convenient access to the underlying C++ is achieved using typeclasses and type-level programming to emulate OO classes and multiple dispatch. This approach makes Haddock very unhappy and the generated documentation is frequently unhelpful. For instance, newcomers to this library are urged not to look at Graphics.UI.FLTK.LowLevel.Dispatch or Graphics.UI.FLTK.LowLevel.Hierarchy. The purpose of this guide is to point users in a more useful direction.
The documentation provided with this API is not yet self-contained and is meant to be used in tandem with the C++ documentation. The rest of this document is about how the Haskell functions and datatypes map to the C++ ones.
Widget Construction
Each widget has its own module all of which are listed below under the Widgets heading . Most modules include a function named `<widgetName>New` that returns a reference to that widget. Although the user does not have to deal with raw pointers directly it might help to understand that this reference is a pointer to a void pointer to a C++ object.
For instance, windowNew
creates a Ref
Window
which is a pointer to a
C++ object of type `Fl_Window`, the FLTK class that knows how to draw,
display and handle window events.
This value of type Ref
Window
is then passed along to various functions
which, transparent to the user, extract the pointer and pass it to the
appropriate `Fl_Window` instance method.
Widget Methods
The Haskell functions that bind to the instance methods of an FLTK class are
listed under the Functions heading in that widget's module. It's worth
remembering that these type signatures associated with the functions listed
under the Functions heading are not the real ones but are artifically
generated because they are much more helpful to users. For instance, the
actual type of activate
exposes all the type level arithmetic required so
it can be used by subclasses of Widget
but is pretty unhelpful as a
reference compared to the artificial type under Functions heading of
Graphics.UI.FLTK.LowLevel.Widget.
Unfortunately to see this more helpful type signature the poor reader has to navigate to the corresponding widget's module, find the Functions header and scroll down to the desired function. Haddock, unfortunately, does not support anchors that link to a named point in the page. I'm very open to ideas on how to make this easier.
Carrying on the previous example from the Widget Creation section, the
methods on a Ref
Window
widget are documented in
Graphics.UI.FLTK.LowLevel.Window under Functions. Each function takes
the Ref
Window
reference as its first argument followed by whatever else
it needs and delegates it appropriately.
As this is a low-level binding the Haskell functions are kept as close as possible in name and argument list to the underlying C++. This allows users familiar with the FLTK API to use this library with less learning overhead and it lets newcomers to FLTK take advantage of the already extensive C++ documentation.
Functions are named to make it as easy as possible to find the corresponding C++ function, however there are some naming conventions to keep in mind:
- setters and getters are prefixed with set and get respectively. In C++ both have the same name; the setter takes an argument while the getter does not. Since Haskell does not support overloading this convention is used.
- in many cases C++ uses overloading to provide default values to
arguments. Since Haskell does not support overloading, these arguments are
Maybe
types eg. thehotspot
function in Graphics.UI.FLTK.LowLevel.Window. In other cases where the common use case leaves the default arguments unspecified the binding provides two functions, a longer less-convenient-to-type one that takes the default argument and a short one that does not eg.drawBox
anddrawBoxWithBoxtype
, also in Graphics.UI.FLTK.LowLevel.Window. - error codes are
Either
types - function arguments which are pointers to be filled are not exposed to the
API user. For instance, a common C++ idiom is to return a string by taking a
pointer to some initialized but empty chunk of memory and filling it up. The
corresponding Haskell function just returns a
Text
.
It is hoped that until the documentation becomes more self-sustaining the user can use these heuristics (and the type signatures) along with the official FLTK documentation to "guess" what the binding functions do.
Widget Hierarchy
Every widget module in the API has a Hierarchy heading that shows all its parents.
The design of the API makes all the parent functions transparently available to that widget. This is also the reason why the actual type signatures are so complicated requiring the manual generation of artificial type signatures.
For instance, the Functions section under
Graphics.UI.FLTK.LowLevel.Window shows that a Ref
Window
can be passed
to getModal to check if the window is modal, but it can also be passed to
children in Graphics.UI.FLTK.LowLevel.Group which counts up the number of
widgets inside the Window
and getX in Graphics.UI.FLTK.LowLevel.Widget
which returns the X coordinate of the Window'
s top-left hand corner.
The hierarchy corresponds almost exactly to the underlying C++ class hierarchy so, again, the user is able to take advantage of the C++ documentation to use the binding API.
Overriding C++ methods
The binding API allows a limited but powerful form of "inheritance" allowing users to override certain key FLTK methods with Haskell functions. All GUI elements that derive from the C++ base class Fl_Widget and the Haskell analog Widget now allow Haskell functions to be passed at widget construction time that give Haskell complete control on drawing, handling, resizing and other key functions. This means that the Haskell user has complete control of look-and-feel and the event loop. The table demos are an example of drawing in Haskell. An example of taking over the event loop is an FLTKHS proof-of-concept that overrides the FLTKHS event loop with the Reflex FRP allowing native functional reactive programming. The sky is the limit!
When providing custom methods the object constructor is no longer `<widgetName>New` but `<widgetName>Custom`, which, in addition to the parameters taken by `<widgetName>New` also takes records of Haskell functions which are then passed to the C++ side.
Much like a callback, the Haskell functions are passed as function pointers to the C++ side and called whenever the event loop deems appropriate. Unlike callbacks, they can be set only on object instantiation.
An example of this is Graphics.UI.FLTK.LowLevel.Widget which, since it is a
base class for most widgets and doesn't have much functionality of its own,
only allows custom construction using widgetCustom
. This constructor takes
a CustomWidgetFuncs
datatype which is a record of functions which tells a
Graphics.UI.FLTK.LowLevel.Widget how to handle events and draw, resize and
display itself.
Again Graphics.UI.FLTK.LowLevel.Window can be used a motivating example.
Its custom constructor windowCustom
, in fact, takes two records. A
CustomWidgetFuncs
which allows the user to override methods in its
Graphics.UI.FLTK.LowLevel.Widget parent class and also a
CustomWindowFuncs
record which allows the user to override flush, a
method on the Window class which tells the window how to force a redraw. For
example, the demo src/Examples/doublebuffer.hs (which corresponds to the
executable 'ftlkhs-doublebuffer') tells both windows how to draw themselves
in a Haskell function that uses low-level FLTK drawing routines by overriding
the draw function of their Graphics.UI.FLTK.LowLevel.Widget parent.
Every widget that supports customizing also provides a default function
record that can be passed to the constructor. For example,
Graphics.UI.FLTK.LowLevel.Widget provides defaultCustomWidgetFuncs
and
Graphics.UI.FLTK.LowLevel.Window has defaultCustomWindowFuncs
. In the
demo mentioned above the singleWindowCustom
function is given
defaultCustomWidgetFuncs
but with an overridden drawCustom
.
Another case where customization comes up a lot is when using Graphics.UI.FLTK.LowLevel.Table which is a low-level table widget that needs to be told, for example, how to draw its cells. The demo src/Examples/table-simple.hs (corresponding to the executable 'fltkhs-table-simple') shows this in action.
Hopefully the demos just mentioned and others included with this library show that even though customizing is limited it is possible to do a lot.
Additionally for most customizable functions a widget provides a corresponding function that calls the default C++ implementation as the function name suffixed with "Super". For instance, the user can provide a custom implementation of handle on Graphics.UI.FLTK.LowLevel.Window via the constructor and every call to handle invokes that implementation, but the user can also call handleSuper to get at the default C++ implementation. This comes in handy when the custom function is just setting up variables or logging and wants underlying implementation to take over at some point.
Warning: since calls to the default implementation of the function, handleSuper, for example, are available to the widget they are automatically available to all subclasses of the widget. I acknowledge this is a confusing aspect of the library's design but hopes the benefit of being able to get at the default implementation out-weighs the trap the user might fall into. If this becomes a pervasive problem I'm open to removing this functionality.
Slow Compilation Issues
As described above, the API emulates multiple dispatch using type-level programming, closed type families and typeclasses. While this is makes for a nice API it has also slowed down compilation of executables much more than expected.
To clarify the time taken to compile the library itself has not changed, but applications that use the library to create executables are taking a lot longer to compile. To further emphasize, there does not appear to be any runtime performance issues. This is only a compile time problem.
To preserve the user's sanity and mine a flag fastCompile
has been
introduced to the skeleton projects, the fltkhs-demos and
to the fltkhs-fluid-demos.
This flag which tells the compiler to skip some steps when
compiling executables, dramatically decreases compile time but also bloats
the resulting executable size and probably makes runtime performance much
slower. In this package and fltkhs-fluid-demos
it is enabled by default, since the executables are just
demos that are not meant to show off performance. To disable this flag, tell
Stack to ignore it during the build
step:
> stack build --flag fltkhs:bundled --flag fltkhs-demos:-fastCompile
In the fltkhs and the
fltkhs-fluid project
skeletons this flag is disabled by default to provide the best runtime
performance. To enable the flag for a smoother development workflow, tell
Stack to enable it during the configure
step:
> stack build --flag fltkhs:bundled --flag fltkhs-hello-world:fastCompile
File Layout
Root - c-src -- The C bindings - c-examples -- demos written using the C bindings (not installed) - fltk-<version>.tar.gz -- The bundled FLTK library - src - TestPrograms -- Haskell test programs - Fluid -- The Fluid file to Haskell conversion utility - Graphics - UI - FLTK - LowLevel -- Haskell bindings - scripts -- various helper scripts (probably not interesting to anyone but myself)
Running in the REPL
Running GUIs in GHCi is fully supported. Using the hello world skeleton as an example the following steps will run it in the REPL:
> git clone http://github.com/deech/fltkhs-hello-world > cd fltkhs-hello-world > stack build --flag fltkhs:bundled > stack ghci --flag fltkhs:bundled fltkhs-hello-world:exe:fltkhs-hello-world [1 of 1] Compiling Main ... Ok, modules loaded: Main ... Loaded GHCi configuration ... Prelude Main> replMain
Unfortunately since FLTKHS is hybrid Haskell/C++ there are limitations compared to running a plain 'ol Haskell library on the REPL:
- The 'stack build ...' is an essential first step before running 'stack ghci ...'. The reason is it uses '-fobject-code' to link in all the C++ libraries which must be built first.
The use of
replMain
instead of just ':main' as you might expect. This is because- it allows closing the GUI to correctly return control to the REPL prompt and
- typing 'Ctrl-C' also correctly hands control back to the REPL.
With just ':main' (1) works but (2) results in a "ghosted" UI where the
GUI window is still visible but unable to accept any keyboard/mouse
input. The reason for the ghosted GUI is that ':main' delegates to the
FLTK C++ event loop which is unable to listen for user interrupts on
the Haskell side and so has no of knowing that it should destroy
itself.replMain
emulates the event loop on the Haskell side allowing
it to stop, clean up and return control when it catch
es a
UserInterrupt
. Thus the replMain
is slower than the optimized C++
event loop but hopefully that's not too big an impediment for REPL
work.