diff options
author | Camil Staps | 2019-11-01 18:41:10 +0100 |
---|---|---|
committer | Camil Staps | 2019-11-01 18:41:10 +0100 |
commit | 0497dfb79211aa1e64304fa446d230c41ca73daa (patch) | |
tree | cc29910a32b81db4430aff1a74172174039e9c25 | |
parent | Remove dependency on supporting C code (diff) |
Cleanup and add documentation
-rw-r--r-- | src/Gtk.dcl | 29 | ||||
-rw-r--r-- | src/Gtk/Internal.dcl | 9 | ||||
-rw-r--r-- | src/Gtk/Shares.dcl | 17 | ||||
-rw-r--r-- | src/Gtk/Signal.dcl | 57 | ||||
-rw-r--r-- | src/Gtk/Signal.icl | 58 | ||||
-rw-r--r-- | src/Gtk/State.dcl | 44 | ||||
-rw-r--r-- | src/Gtk/Tune.dcl | 16 | ||||
-rw-r--r-- | src/Gtk/Tune.icl | 8 | ||||
-rw-r--r-- | src/Gtk/Types.dcl | 25 | ||||
-rw-r--r-- | src/Gtk/Widgets.dcl | 185 | ||||
-rw-r--r-- | src/Gtk/Widgets.icl | 64 | ||||
-rw-r--r-- | src/Gtk/Widgets/Sheet.dcl | 13 | ||||
-rw-r--r-- | src/Gtk/Widgets/Sheet/Signal.dcl | 7 | ||||
-rw-r--r-- | src/Gtk/Widgets/Sheet/Signal.icl | 6 |
14 files changed, 441 insertions, 97 deletions
diff --git a/src/Gtk.dcl b/src/Gtk.dcl index 964d2a4..9728d3e 100644 --- a/src/Gtk.dcl +++ b/src/Gtk.dcl @@ -1,5 +1,34 @@ definition module Gtk +/** + * This library provides an application framework based on the Gtk+ 3 GUI + * toolkit. For information about the C library, see the documentation at + * https://developer.gnome.org/gtk3/stable/. + * + * For a low-level interface to Gtk, see `Gtk.Internal`. This module can be + * used without dependencies on the framework, which provides additional + * abstractions. + * + * The application framework is monadic; see `Gtk.State`. A typical application + * will consist of a setup function which populates the initial window and sets + * up signals for buttons, menu items, etc. The setup function is run with + * `runGtk` (`Gtk.State`), which then enters the Glib main loop to handle + * events. Signal handlers are Clean functions and may modify the monadic + * state. + * + * For an overview of the available GUI elements, see `Gtk.Widgets`. The + * relevant signals are provided in `Gtk.Signals`. Check the reference manual + * to see which signals are emitted for which types of widgets. + * + * NB: this library is not intended to be an exhaustive interface to Gtk. New + * functionality is added on a by-need basis. There is, however, enough example + * code available in these modules to be able to quickly add support for so far + * unsupported parts of Gtk. + * + * A simple share system, based on that of iTasks, is provided in `Gtk.Shares` + * and allows the programmer to easily keep shared data. + */ + import Gtk.Shares import Gtk.Signal import Gtk.State diff --git a/src/Gtk/Internal.dcl b/src/Gtk/Internal.dcl index 5b4d55d..8950cf9 100644 --- a/src/Gtk/Internal.dcl +++ b/src/Gtk/Internal.dcl @@ -1,5 +1,14 @@ definition module Gtk.Internal +/** + * This module provides low-level access to the Gtk+ 3 library. If you use the + * abstractions exported by the `Gtk` module, you should not need to use it + * directly. + * + * For documentation, see the Gtk+ 3 reference manual: + * https://developer.gnome.org/gtk3/stable/ + */ + from StdMaybe import :: Maybe from System._Pointer import :: Pointer diff --git a/src/Gtk/Shares.dcl b/src/Gtk/Shares.dcl index d1e5556..48db8eb 100644 --- a/src/Gtk/Shares.dcl +++ b/src/Gtk/Shares.dcl @@ -1,20 +1,35 @@ definition module Gtk.Shares +/** + * This module provides shares in the `GtkM` monad, allowing the programmer to + * abstract from global state. They are loosely based on SDSs in iTasks, but + * much less advanced. + */ + from Gtk.State import :: GtkM +//* Identifier of a share. :: ShareId :== String -//* Exported to have a TC instance -- do not use directly! +//* Exported to have a TC instance --- do not use directly! :: Shared a :== (String,a) +//* Types must instantiate this class to be able to be shared. class shared a | TC a +//* Sets up a share with a default value (cf. iTasks' `sharedStore`). share :: !ShareId a -> Shared a | shared a getShared :: !(Shared a) -> GtkM a | shared a setShared :: !(Shared a) !a -> GtkM a | shared a updateShared :: !(a -> a) !(Shared a) -> GtkM a | shared a +/** + * A share with an initialization function. It will always return the same + * value, namely the result of the initialization function, but that function + * will only be evaluated once. It can be seen as an impure CAF. + */ singletonShared :: !String !(GtkM a) -> GtkM a | shared a +//* Evaluate a function with a temporary share. withShared :: a !((Shared a) -> GtkM b) -> GtkM b | shared a diff --git a/src/Gtk/Signal.dcl b/src/Gtk/Signal.dcl index fa9babf..85a780c 100644 --- a/src/Gtk/Signal.dcl +++ b/src/Gtk/Signal.dcl @@ -1,5 +1,9 @@ definition module Gtk.Signal +/** + * This module provides functionality for Gtk signals. + */ + from System._Pointer import :: Pointer from Gdk.Events import :: GdkEvent @@ -8,13 +12,19 @@ from Gtk.Tune import class tune from Gtk.Types import :: GtkPropagate, :: GtkTimeout from Gtk.Widgets import class gtkWidget +/** + * If more handlers are defined outside this module (and hence outside the + * `GSignalHandler` type), they must instantiate this class to be able to be + * installed on widgets. + */ class signalHandler h where + //* The name of the signal, e.g. `destroy` or `key-press-event`. signalName :: !h -> String + //* An internal representation of the handler. signalHandler :: !h -> SignalHandlerInternal -:: SignalHandler = E.h: SignalHandler h & signalHandler h - +//* A number of basic signals. See the Gtk documentation for their usage. :: GSignalHandler = ActivateHandler !(GtkM ()) | ChangedHandler !(GtkM ()) @@ -29,28 +39,57 @@ where instance signalHandler GSignalHandler +/** + * Inner representation of the various types of signal handlers that there are. + * This is only needed outside this module when adding more signals. + */ :: SignalHandlerInternal = SHI_Void !(GtkM ()) | SHI_Pointer_Bool !(Pointer -> GtkM Bool) | SHI_Int_Int_Bool !(Int Int -> GtkM Bool) | SHI_Int_Int_Pointer_Pointer_Bool !(Int Int Pointer Pointer -> GtkM Bool) +/** + * Install a signal handler on a widget. Often, the `tune` instance defined + * below leads to more readable code. + */ installSignalHandler :: !h !w -> GtkM w | signalHandler h & gtkWidget w -instance tune w SignalHandler | gtkWidget w +//* Alternative for `installSignalHandler` for more readable code. instance tune w GSignalHandler | gtkWidget w -saveState :: GtkM () -retrieveState :: GtkM GtkState +/** + * Run a function at a certain interval. The function will continue to run + * until it returns `False`. Note that Glib timeouts cannot be used for precise + * timing; see the documentation for more details: + * https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-timeout-add + */ +addTimeout :: !GtkTimeout !(GtkM Bool) -> GtkM () -//* Wrap functionality in `saveState` and `retrieveState` if it can be re-entrant. +/** + * Wrap functionality with `saveState` and `retrieveState`. This is needed if + * it can be re-entrant, for example when it can trigger signals. See the + * documentation on `saveState` for more details. + */ withPossibleCallback :: !(GtkM a) -> GtkM a -// Only for foreign export: +/** + * Some functions can lead to Gtk signals to be emitted. If these signals have + * handlers set up, the function is re-entrant (may return to Clean code). For + * this kind of functions, we need to save the `GtkM` state beforehand and + * retrieve it afterwards, because the signal handler may rely on that state + * and may modify it. `saveState` saves the state internally; `retrieveState` + * restores it. `withPossibleCallback` is a convenient wrapper combining both. + */ +saveState :: GtkM () + +//* Retrieve the state saved with `saveState`. +retrieveState :: GtkM GtkState + +// The functions below are only exported because they need a foreign export entry point: handleSignal_void :: !Pointer !Int -> Int handleSignal_pointer_bool :: !Pointer !Pointer !Int -> Int handleSignal_int_int_bool :: !Pointer !Int !Int !Int -> Int handleSignal_int_int_pointer_pointer_bool :: !Pointer !Int !Int !Pointer !Pointer !Int -> Int -addTimeout :: !GtkTimeout !(GtkM Bool) -> GtkM () -handleTimeout :: !Int -> Int // only for foreign export +handleTimeout :: !Int -> Int diff --git a/src/Gtk/Signal.icl b/src/Gtk/Signal.icl index 254523a..543c7a2 100644 --- a/src/Gtk/Signal.icl +++ b/src/Gtk/Signal.icl @@ -76,14 +76,37 @@ where pushLc handleSignal_int_int_pointer_pointer_bool } -instance tune w SignalHandler | gtkWidget w -where - tune (SignalHandler handler) widget = installSignalHandler handler widget - instance tune w GSignalHandler | gtkWidget w where tune handler widget = installSignalHandler handler widget +addTimeout :: !GtkTimeout !(GtkM Bool) -> GtkM () +addTimeout interval callback = + modState (\st -> + let id = st.timeout_counter+1 in + { st + & timeouts = 'Data.Map'.put id (wrapped_callback id) st.timeouts + , timeout_counter = id + }) >>= \{timeout_counter=id} -> + case interval of + Milliseconds ms -> toState (g_timeout_add ms get_handleTimeout_address id) + Seconds s -> toState (g_timeout_add_seconds s get_handleTimeout_address id) +where + wrapped_callback id = + callback >>= \r -> + if r + getState + (modState \st -> {st & timeouts='Data.Map'.del id st.timeouts}) >>| + pure r + + get_handleTimeout_address :: Pointer + get_handleTimeout_address = code { + pushLc handleTimeout + } + +withPossibleCallback :: !(GtkM a) -> GtkM a +withPossibleCallback m = saveState >>| m >>= \r -> retrieveState >>| pure r + // NB: low-level hacking to use and modify the GtkState from within callbacks. // We use a CAF to keep track of the state. In runGtk, the state is saved with // saveState. This state is retrieved with retrieveState there (to check @@ -111,9 +134,6 @@ saveState = getState >>= \state -> toState (save_state state) retrieveState :: GtkM GtkState retrieveState = modState (const saved_state.[0]) -withPossibleCallback :: !(GtkM a) -> GtkM a -withPossibleCallback m = saveState >>| m >>= \r -> retrieveState >>| pure r - foreign export handleSignal_void handleSignal_void :: !Pointer !Int -> Int handleSignal_void _ id = handleSignal id \h -> case h of @@ -159,30 +179,6 @@ handleSignal id handle # (r,st) = f st -> save_state st r -addTimeout :: !GtkTimeout !(GtkM Bool) -> GtkM () -addTimeout interval callback = - modState (\st -> - let id = st.timeout_counter+1 in - { st - & timeouts = 'Data.Map'.put id (wrapped_callback id) st.timeouts - , timeout_counter = id - }) >>= \{timeout_counter=id} -> - case interval of - Milliseconds ms -> toState (g_timeout_add ms get_handleTimeout_address id) - Seconds s -> toState (g_timeout_add_seconds s get_handleTimeout_address id) -where - wrapped_callback id = - callback >>= \r -> - if r - getState - (modState \st -> {st & timeouts='Data.Map'.del id st.timeouts}) >>| - pure r - - get_handleTimeout_address :: Pointer - get_handleTimeout_address = code { - pushLc handleTimeout - } - foreign export handleTimeout handleTimeout :: !Int -> Int handleTimeout id diff --git a/src/Gtk/State.dcl b/src/Gtk/State.dcl index c4e17a2..1dde9d1 100644 --- a/src/Gtk/State.dcl +++ b/src/Gtk/State.dcl @@ -1,5 +1,10 @@ definition module Gtk.State +/** + * This module provides types and functions to set up the monadic state of a + * Gtk program and run an application. + */ + from StdMaybe import :: Maybe from Control.Applicative import class pure, class <*>, class Applicative @@ -10,6 +15,10 @@ from Data.Map import :: Map from Gtk.Shares import :: ShareId from Gtk.Signal import :: SignalHandlerInternal +/** + * In the internal state of the `GtkM` monad. It is exported for use in other + * modules of the Gtk library; it should not normally be used by applications. + */ :: GtkState = { world :: !() , return :: !Bool @@ -17,9 +26,10 @@ from Gtk.Signal import :: SignalHandlerInternal , signal_counter :: !Int , timeouts :: !Map Int (GtkM Bool) , timeout_counter :: !Int - , shares :: !Map ShareId Dynamic // TODO: make this map strict to be able to free references in it + , shares :: !Map ShareId Dynamic } +//* The Gtk state monad. :: GtkM a =: GtkM (GtkState -> (a, GtkState)) instance Functor GtkM @@ -27,18 +37,50 @@ instance pure GtkM instance <*> GtkM instance Monad GtkM +//* A new, empty state. newGtkState :: GtkState +//* Run a Gtk monad in a new, empty state. runGtk :: !(GtkM a) !*World -> (!a, !*World) +//* Get the internal state. getState :: GtkM GtkState +//* Modify the internal state. modState :: !(GtkState -> GtkState) -> GtkM GtkState +/** + * Run a function in the `GtkM` monad and discard its result. This function is + * typically used with functions from `Gtk.Internal` and is not normally used + * directly. + */ toState :: !(A.a: a -> a) -> GtkM () + +/** + * Run a function in the `GtkM` monad and return its result. This function is + * typically used with functions from `Gtk.Internal` and is not normally used + * directly. + */ toStateR :: !(A.a: a -> (r,a)) -> GtkM r +//* Apply a function with side effects in the `GtkM` monad. appWorld :: !(*World -> *World) -> GtkM () +//* Apply a function with side effects in the `GtkM` monad; return its result. accWorld :: !(*World -> (r,*World)) -> GtkM r +/** + * Request graceful termination of the program. This sets the `return` field + * of the `GtkState`, after which `runGtk` will terminate before the next + * iteration. + */ quit :: GtkM () + +/** + * Run the Glib main loop as long as there are events pending. This may be + * useful before starting a long blocking operation in Clean, such that all + * updates to the views have been applied. You can thus set text in a status + * bar or show a spinner before the blocking operation starts. + * + * Unfortunately, threading or Glib's background task mechanism is not + * implemented, so the blocking operation will still freeze the GUI. + */ runWhileEventsPending :: GtkM () diff --git a/src/Gtk/Tune.dcl b/src/Gtk/Tune.dcl index f5966ec..63e48a4 100644 --- a/src/Gtk/Tune.dcl +++ b/src/Gtk/Tune.dcl @@ -1,12 +1,18 @@ definition module Gtk.Tune +/** + * This module provides a class and useful infix operators for 'tuning' things. + * Tuning involves modifying an existing element, typically a widget. A single + * `tune` class avoids the need for a variety of `set...` functions. + */ + from Gtk.State import :: GtkM -from Gtk.Types import :: GtkCSSClass, :: GtkMargins -from Gtk.Widgets import :: GtkWidget, class gtkWidget +//* Modify `elem` with `option`. class tune elem option :: !option !elem -> GtkM elem +/** + * Infix operator to tune something, typically used when creating the element. + * For example: `newTextView <<@ Insensitive >>= \text_view -> ...`. + */ (<<@) infixl 2 :: !(GtkM elem) !option -> GtkM elem | tune elem option - -instance tune w GtkMargins | gtkWidget w -instance tune w GtkCSSClass | gtkWidget w diff --git a/src/Gtk/Tune.icl b/src/Gtk/Tune.icl index a275ea2..f4553bc 100644 --- a/src/Gtk/Tune.icl +++ b/src/Gtk/Tune.icl @@ -9,11 +9,3 @@ import Gtk (<<@) infixl 2 :: !(GtkM elem) !option -> GtkM elem | tune elem option (<<@) elemf option = elemf >>= tune option - -instance tune w GtkMargins | gtkWidget w -where - tune margins widget = setMargins margins widget - -instance tune w GtkCSSClass | gtkWidget w -where - tune cls widget = addCSSClass cls widget diff --git a/src/Gtk/Types.dcl b/src/Gtk/Types.dcl index 9dac38d..1e2ed78 100644 --- a/src/Gtk/Types.dcl +++ b/src/Gtk/Types.dcl @@ -1,5 +1,17 @@ definition module Gtk.Types +/** + * This module provides common types for the Gtk library. + * + * Most of these types map directly to Glib, Gdk, or Gtk `enum`s --- for more + * explanation, see the Gtk+ 3 reference manual: + * https://developer.gnome.org/gtk3/stable/ + * + * Some types, like `GtkExpand`, map to booleans in the C libraries, and are + * merely provided for more readable Clean code (in the case of `GtkExpand` for + * example for `packBox`). + */ + from StdOverloaded import class fromInt, class toInt :: GType @@ -110,8 +122,17 @@ instance toInt GtkJustification , bottom :: !Int } +//* Creates a `GtkMargins` record where all margins are the same. margin :: !Int -> GtkMargins +/** + * Used to avoid multiple `setMarkup` functions. This is like `GtkText`, but + * assumes that the string contains Pango markup. For an overview of the Pango + * markup language, see the documentation: + * https://developer.gnome.org/pygtk/stable/pango-markup-language.html + */ +:: GtkMarkup =: Markup String + :: GtkMessageType = InfoMessage | WarningMessage @@ -186,6 +207,10 @@ instance toInt GtkScrollbarPolicy instance toInt GtkStylePriority +/** + * Used to avoid multiple `setText` functions. + * The string is assumed to be valid UTF-8. + */ :: GtkText =: Text String :: GtkTimeout diff --git a/src/Gtk/Widgets.dcl b/src/Gtk/Widgets.dcl index 65f8c87..957838b 100644 --- a/src/Gtk/Widgets.dcl +++ b/src/Gtk/Widgets.dcl @@ -1,5 +1,25 @@ definition module Gtk.Widgets +/** + * This module provides functionality for common widgets in the Gtk library. + * See the Gtk+ 3 reference manual for more details about what they are used + * for: https://developer.gnome.org/gtk3/stable/ + * + * Also see the widget gallery for a quick overview of the different widgets: + * https://developer.gnome.org/gtk3/stable/ch03.html + * + * The Gtk library is object-oriented. This is mirrored here with classes. For + * example, all subclasses of `GtkWidget` implement the `gtkWidget` class, and + * common methods of the `GtkWidget` class correspond to overloaded functions + * in Clean. + * + * NB: The types of widgets are exported here because (1) newtypes cannot be + * abstract and (2) they must be able to implement `TC` so that they can be + * shared (see `Gtk.Shares`). When the compiler supports abstract newtypes and + * `TC` instantiation of hidden types, the implementation of these types may + * be removed from the definition module; do not rely on it! + */ + from StdMaybe import :: Maybe from System.FilePath import :: FilePath @@ -12,29 +32,49 @@ from Gtk.Types import :: GdkModifier, :: GtkAlign, :: GtkButtonsType, :: GtkCompletionMode, :: GtkCSSClass, :: GtkDirection, :: GtkExpand, :: GtkFileChooserAction, :: GtkMargins, - :: GtkMessageType, :: GtkModal, :: GtkOrientation, :: GtkPanedHandleWidth, - :: GtkResize, :: GtkResponse, :: GtkScrollbarPolicy, :: GtkSensitivity, - :: GtkShrink, :: GtkSizeRequest, :: GtkSpacing, :: GtkStylePriority, - :: GtkText, :: GtkTitle, :: GtkWrapMode - + :: GtkMarkup, :: GtkMessageType, :: GtkModal, :: GtkOrientation, + :: GtkPanedHandleWidth, :: GtkResize, :: GtkResponse, + :: GtkScrollbarPolicy, :: GtkSensitivity, :: GtkShrink, :: GtkSizeRequest, + :: GtkSpacing, :: GtkStylePriority, :: GtkText, :: GtkTitle, :: GtkWrapMode + +/** + * A `GtkAccelGroup` is needed for a `GtkAccelerator`. 'Accelerator' is Gtk's + * term for what most people call shortcuts, i.e. `Ctrl`-`S`, etc. + */ :: GtkAccelGroup =: GtkAccelGroup Pointer newAccelGroup :: !w -> GtkM GtkAccelGroup | gtkWindow w +/** + * An accelerator is what people normally call a shortcut (e.g. `Ctrl`-`S`). + * The `String` is the name of the key that is pressed. See `gdkkeysyms.h` and + * remove the `GDK_KEY_` prefix; hence e.g. `s` for the letter `s` or `Return` + * for the return/enter key. + */ :: GtkAccelerator = Accelerator !GtkAccelGroup !String ![GdkModifier] +//* An action bar typically holds a number of buttons. :: GtkActionBar =: GtkActionBar Pointer instance gtkWidget GtkActionBar newActionBar :: GtkM GtkActionBar + +//* Add `w` to the start or end of the action bar. packActionBar :: !GtkActionBar !GtkDirection !w -> GtkM w | gtkWidget w +//* A box holds a number of child widgets in some direction with spacing. :: GtkBox =: GtkBox Pointer instance gtkWidget GtkBox instance gtkContainer GtkBox instance gtkOrientable GtkBox -newBox :: !GtkOrientation !Int -> GtkM GtkBox +newBox :: !GtkOrientation -> GtkM GtkBox + +/** + * Add `w` to the start or the end of the box. When used with `Expand`, + * additional space for the box is given to this child (and all other children + * with `Expand` set). + */ packBox :: !GtkBox !GtkDirection !GtkExpand !w -> GtkM w | gtkWidget w instance tune GtkBox GtkSpacing @@ -42,6 +82,12 @@ instance tune GtkBox GtkSpacing :: GtkButton =: GtkButton Pointer instance gtkWidget GtkButton +/** + * Create a button with a single icon. See the icon naming specification for a + * list of common icon names: + * https://developer.gnome.org/icon-naming-spec/ + * You can also see the icons in `/usr/share/icons/<theme>/<size>. + */ newButtonFromIconName :: !String -> GtkM GtkButton :: GtkContainer =: GtkContainer Pointer @@ -53,6 +99,7 @@ instance gtkContainer GtkContainer addToContainer :: !c !w -> GtkM w | gtkWidget w & gtkContainer c +//* A dialog is a popup window. :: GtkDialog =: GtkDialog Pointer class gtkDialog a :: !a -> GtkDialog @@ -62,18 +109,39 @@ instance gtkContainer GtkDialog instance gtkWindow GtkDialog instance gtkDialog GtkDialog +//* If a dialog is `Modal` it prevents interaction with the underlying window. instance tune d GtkModal | gtkDialog d newDialog :: !GtkWindow -> GtkM GtkDialog + +/** + * Runs a dialog, returning when a button has been activated or the dialog has + * been closed somehow (typically by pressing `Esc`). Note that you still need + * to `destroy` the dialog (this allows you to not close the dialog and show an + * error message, if appropriate). + */ runDialog :: !d -> GtkM GtkResponse | gtkDialog d +/** + * Add a button with the given `String` as text to the dialog. When pressed, + * the dialog returns the given `GtkResponse`. + */ addButton :: !String !GtkResponse !d -> GtkM GtkButton | gtkDialog d getContentArea :: !d -> GtkBox | gtkDialog d +//* Convenience function to create a simple message dialog. newMessageDialog :: !GtkWindow !GtkMessageType !GtkButtonsType !String -> GtkM GtkDialog + +/** + * Convenience function to select a file or folder. The optional `String` is + * the title for the dialog. This function internally calls `runDialog` and + * returns the chosen file path, or `Nothing` if the dialog has been cancelled + * somehow. + */ getFileWithDialog :: !GtkWindow !GtkFileChooserAction !(Maybe String) -> GtkM (Maybe FilePath) +//* An entry is a simple text field. Subclasses allow more advanced input. :: GtkEntry =: GtkEntry Pointer class gtkEntry a :: !a -> GtkEntry @@ -87,50 +155,97 @@ getText :: !e -> GtkM String | gtkEntry e instance tune GtkEntry GtkText instance tune GtkEntry GtkEntryCompletion +/** + * See the Gtk documentation an overview of user input completion: + * https://developer.gnome.org/gtk3/stable/GtkEntryCompletion.html#GtkEntryCompletion.description + * + * We do not provide all features here, but assume the common use case of a + * `GtkListStore` that is filtered with the default match function. It is + * possible to tune the completion method with a `GtkCompletionMode`, however. + */ :: GtkEntryCompletion =: GtkEntryCompletion Pointer newEntryCompletion :: GtkM GtkEntryCompletion + +//* Sets the column of the `GtkListStore` to be used for completion. setTextColumn :: !Int !GtkEntryCompletion -> GtkM GtkEntryCompletion instance tune GtkEntryCompletion GtkListStore instance tune GtkEntryCompletion GtkCompletionMode +//* A frame holds a single child, with a border and an optional title. :: GtkFrame =: GtkFrame Pointer instance gtkWidget GtkFrame instance gtkContainer GtkFrame +//* Create a new frame with `w` as the child. newFrame :: !GtkTitle !w -> GtkM GtkFrame | gtkWidget w +//* Convenience function to create a widget and a frame around it at once. framed :: !GtkTitle !(GtkM w) -> GtkM (w, GtkFrame) | gtkWidget w +/** + * A grid is a generalized table, allowing outlining child widget positions to + * each other. + */ :: GtkGrid =: GtkGrid Pointer instance gtkWidget GtkGrid newGrid :: GtkM GtkGrid + +/** + * Attach `w` to the given grid. The first `(Int,Int)` argument is the position + * in the grid (where the top left is `(0,0)`); the second `(Int,Int)` argument + * is the number of rows and columns that the widget occupies. + */ attachGrid :: !GtkGrid !(!Int,!Int) !(!Int,!Int) !w -> GtkM w | gtkWidget w +//* A label is used to display a small or medium amount of text. :: GtkLabel =: GtkLabel Pointer instance gtkWidget GtkLabel newLabel :: GtkM GtkLabel instance tune GtkLabel GtkText +/** + * A list store holds values in a list. It is used in several places, like for + * `GtkEntryCompletion` and `GtkTreeView`. + */ :: GtkListStore =: GtkListStore Pointer +//* Create a new list store where rows are lists of values of the given types. newListStore :: ![GType] -> GtkM GtkListStore + +//* Remove all items from the list store. clearListStore :: !GtkListStore -> GtkM GtkListStore + +//* Add a new value to the end of the list store. appendToListStore :: ![GValue] !GtkListStore -> GtkM GtkListStore + +//* Swap the items at the given indices in the list store, returning success. swapItems :: !Int !Int !GtkListStore -> GtkM Bool +/** + * A menu provides actions to the user. `GtkMenu` by itself is useful to create + * sub-menus (see `setSubMenu`). For a top-level menu, see `GtkMenuBar`. + */ :: GtkMenu =: GtkMenu Pointer instance gtkWidget GtkMenu newMenu :: GtkM GtkMenu +/** + * A menu bar is usually placed at the top of a window, providing actions to + * the user. + */ :: GtkMenuBar =: GtkMenuBar Pointer instance gtkWidget GtkMenuBar newMenuBar :: GtkM GtkMenuBar +/** + * A menu item is an item in a menu. Attach an `ActivateHandler` or set a + * sub-menu with `setSubMenu`. + */ :: GtkMenuItem =: GtkMenuItem Pointer class gtkMenuItem a :: !a -> GtkMenuItem @@ -139,22 +254,31 @@ instance gtkWidget GtkMenuItem instance gtkMenuItem GtkMenuItem newMenuItem :: !String -> GtkM GtkMenuItem + +//* Attach the menu as a sub-menu to `mi`. setSubMenu :: !mi !GtkMenu -> GtkM GtkMenu | gtkMenuItem mi +//* A check menu item is a menu item with a checkbox. :: GtkCheckMenuItem =: GtkCheckMenuItem Pointer instance gtkWidget GtkCheckMenuItem instance gtkMenuItem GtkCheckMenuItem newCheckMenuItem :: !String -> GtkM GtkCheckMenuItem + +//* Check whether the checkbox of the menu item is checked. isActive :: !GtkCheckMenuItem -> GtkM Bool + +//* Modify the checked status of the checkbox of the menu item. setActive :: !Bool !GtkCheckMenuItem -> GtkM GtkCheckMenuItem +//* A separator menu item draws as a simple line to group other items together. :: GtkSeparatorMenuItem =: GtkSeparatorMenuItem Pointer instance gtkWidget GtkSeparatorMenuItem instance gtkMenuItem GtkSeparatorMenuItem newSeparatorMenuItem :: GtkM GtkSeparatorMenuItem +//* This is an interface for widgets that hold a collection of menu items. :: GtkMenuShell =: GtkMenuShell Pointer instance gtkWidget GtkMenuShell @@ -163,12 +287,14 @@ instance gtkMenuShell GtkMenu, GtkMenuBar, GtkMenuShell appendToMenuShell :: !s !mi -> GtkM mi | gtkMenuShell s & gtkMenuItem mi +//* This is an interface for widgets of which the children can be oriented. :: GtkOrientable =: GtkOrientable Pointer class gtkOrientable a :: !a -> GtkOrientable instance tune o GtkOrientation | gtkOrientable o +//* A paned holds two children, with a (usually moveable) handle in between. :: GtkPaned =: GtkPaned Pointer instance gtkWidget GtkPaned instance gtkContainer GtkPaned @@ -177,6 +303,10 @@ newPaned :: !GtkOrientation !GtkPanedHandleWidth -> GtkM GtkPaned packPane1 :: !GtkPaned !GtkResize !GtkShrink !w -> GtkM w | gtkWidget w packPane2 :: !GtkPaned !GtkResize !GtkShrink !w -> GtkM w | gtkWidget w +/** + * A scrolled window holds a single child and uses a scrollbar if it does not + * fit in the outer dimensions. + */ :: GtkScrolledWindow =: GtkScrolledWindow Pointer instance gtkWidget GtkScrolledWindow instance gtkContainer GtkScrolledWindow @@ -186,17 +316,20 @@ newScrolledWindow :: GtkM GtkScrolledWindow instance tune GtkScrolledWindow (GtkScrollbarPolicy, GtkScrollbarPolicy) where tune :: !(!GtkScrollbarPolicy, !GtkScrollbarPolicy) !GtkScrolledWindow -> GtkM GtkScrolledWindow +//* A search entry is a special entry with a search icon and clear button. :: GtkSearchEntry =: GtkSearchEntry Pointer instance gtkWidget GtkSearchEntry instance gtkEntry GtkSearchEntry newSearchEntry :: GtkM GtkSearchEntry +//* A separator is a simple line, allowing for separation between widgets. :: GtkSeparator =: GtkSeparator Pointer instance gtkWidget GtkSeparator newSeparator :: !GtkOrientation -> GtkM GtkSeparator +//* A spinner is an animated widget signaling indefinite progress. :: GtkSpinner =: GtkSpinner Pointer instance gtkWidget GtkSpinner @@ -204,12 +337,14 @@ newSpinner :: GtkM GtkSpinner startSpinner :: !GtkSpinner -> GtkM GtkSpinner stopSpinner :: !GtkSpinner -> GtkM GtkSpinner +//* A text buffer is viewed by a `GtkTextView`; updates occur on this type. :: GtkTextBuffer =: GtkTextBuffer Pointer -setText :: !String !GtkTextBuffer -> GtkM GtkTextBuffer -setMarkup :: !String !GtkTextBuffer -> GtkM GtkTextBuffer +instance tune GtkTextBuffer GtkText +instance tune GtkTextBuffer GtkMarkup insertAtCursor :: !String !GtkTextBuffer -> GtkM GtkTextBuffer +//* A text view holds a larger amount of text than a `GtkLabel`. :: GtkTextView =: GtkTextView Pointer instance gtkWidget GtkTextView instance gtkContainer GtkTextView @@ -219,16 +354,33 @@ getTextBuffer :: !GtkTextView -> GtkTextBuffer instance tune GtkTextView GtkWrapMode +//* A tree view is a view on a `GtkListStore` with customizable columns. :: GtkTreeView =: GtkTreeView Pointer instance gtkWidget GtkTreeView newTreeView :: !GtkListStore -> GtkM GtkTreeView + +/** + * Append a column with a given title to the tree view. The `Int` argument is + * the column index in the list store that is shown. When `Expand` is used, + * excess space of the tree view is (partially) given to this column. + */ appendColumnToTreeView :: !String !Int !GtkExpand !GtkTreeView -> GtkM GtkTreeView +//* Add a signal handler for the event that the user selects a different row. addSelectionChangedHandler :: !(GtkM ()) !GtkTreeView -> GtkM GtkTreeView + +/** + * Gets the path to the currently selected item, or `Nothing` if no item is + * selected. For list stores, this is a singleton list containing the row + * number. + */ getPathToSelection :: !GtkTreeView -> GtkM (Maybe [Int]) + +//* Select an item in the view, by its path (see `getPathToSelection`). selectPath :: ![Int] !GtkTreeView -> GtkM Bool +//* This is an abstract class; every GUI element is a widget. :: GtkWidget =: GtkWidget Pointer class gtkWidget a :: !a -> GtkWidget @@ -237,14 +389,20 @@ instance gtkWidget GtkWidget show :: !w -> GtkM w | gtkWidget w hide :: !w -> GtkM w | gtkWidget w + +//* Check whether this widget and all of its parents are visible. isVisible :: !w -> GtkM Bool | gtkWidget w + +//* Destroy the widget, freeing all related memory. destroy :: !w -> GtkM () | gtkWidget w + +//* Grabs user input focus. This only makes sense for `GtkEntry` and similar. grabFocus :: !w -> GtkM w | gtkWidget w -addCSSClass :: !GtkCSSClass !w -> GtkM w | gtkWidget w removeCSSClass :: !GtkCSSClass !w -> GtkM () | gtkWidget w -setMargins :: !GtkMargins !w -> GtkM w | gtkWidget w +instance tune w GtkCSSClass | gtkWidget w +instance tune w GtkMargins | gtkWidget w instance tune w GtkSensitivity | gtkWidget w instance tune w (GtkAlign,GtkAlign) | gtkWidget w where tune :: !(!GtkAlign, !GtkAlign) !w -> GtkM w | gtkWidget w @@ -253,6 +411,7 @@ where tune :: !(!GtkExpand, !GtkExpand) !w -> GtkM w | gtkWidget w instance tune w GtkAccelerator | gtkWidget w instance tune w GtkSizeRequest | gtkWidget w +//* A window is a top-level element, holding a single child widget. :: GtkWindow =: GtkWindow Pointer class gtkWindow a :: !a -> GtkWindow @@ -263,6 +422,12 @@ instance gtkWindow GtkWindow newPopup :: GtkM GtkWindow newWindow :: GtkM GtkWindow + +/** + * Adds CSS from a local file to the window and all its child widgets. + * @result Success (the operation may fail if the file cannot be found or + * contains illegal syntax). + */ addCSSFromFile :: !GtkStylePriority !FilePath !GtkWindow -> GtkM Bool instance tune w GtkTitle | gtkWindow w diff --git a/src/Gtk/Widgets.icl b/src/Gtk/Widgets.icl index 2c522d0..702ea0e 100644 --- a/src/Gtk/Widgets.icl +++ b/src/Gtk/Widgets.icl @@ -42,9 +42,9 @@ instance gtkWidget GtkBox where gtkWidget (GtkBox b) = GtkWidget b instance gtkContainer GtkBox where gtkContainer (GtkBox b) = GtkContainer b instance gtkOrientable GtkBox where gtkOrientable (GtkBox b) = GtkOrientable b -newBox :: !GtkOrientation !Int -> GtkM GtkBox -newBox orientation spacing = - toStateR (gtk_box_new orientation=:Vertical spacing) >>= \b -> +newBox :: !GtkOrientation -> GtkM GtkBox +newBox orientation = + toStateR (gtk_box_new orientation=:Vertical 0) >>= \b -> show (GtkBox b) packBox :: !GtkBox !GtkDirection !GtkExpand !w -> GtkM w | gtkWidget w @@ -425,19 +425,21 @@ stopSpinner spinner=:(GtkSpinner s) = toState (gtk_spinner_stop s) >>| pure spinner -setText :: !String !GtkTextBuffer -> GtkM GtkTextBuffer -setText s buffer=:(GtkTextBuffer b) = - toState (gtk_text_buffer_set_text b s (size s)) >>| - pure buffer +instance tune GtkTextBuffer GtkText +where + tune (Text s) buffer=:(GtkTextBuffer b) = + toState (gtk_text_buffer_set_text b s (size s)) >>| + pure buffer -setMarkup :: !String !GtkTextBuffer -> GtkM GtkTextBuffer -setMarkup s buffer=:(GtkTextBuffer b) = - toStateR (gtk_text_buffer_get_start_iter b) >>= \start -> - toStateR (gtk_text_buffer_get_end_iter b) >>= \end -> - toState (gtk_text_buffer_delete b start end) >>| - toStateR (gtk_text_buffer_get_start_iter b) >>= \start -> - toState (gtk_text_buffer_insert_markup b start s) >>| - pure buffer +instance tune GtkTextBuffer GtkMarkup +where + tune (Markup s) buffer=:(GtkTextBuffer b) = + toStateR (gtk_text_buffer_get_start_iter b) >>= \start -> + toStateR (gtk_text_buffer_get_end_iter b) >>= \end -> + toState (gtk_text_buffer_delete b start end) >>| + toStateR (gtk_text_buffer_get_start_iter b) >>= \start -> + toState (gtk_text_buffer_insert_markup b start s) >>| + pure buffer insertAtCursor :: !String !GtkTextBuffer -> GtkM GtkTextBuffer insertAtCursor s buffer=:(GtkTextBuffer b) = @@ -540,27 +542,29 @@ grabFocus widget = toState (gtk_widget_grab_focus w) >>| pure widget -addCSSClass :: !GtkCSSClass !w -> GtkM w | gtkWidget w -addCSSClass (Class cls) widget = - let (GtkWidget w) = gtkWidget widget in - toStateR (gtk_widget_get_style_context w) >>= \context -> - toState (gtk_style_context_add_class context cls) >>| - pure widget - removeCSSClass :: !GtkCSSClass !w -> GtkM () | gtkWidget w removeCSSClass (Class cls) widget = let (GtkWidget w) = gtkWidget widget in toStateR (gtk_widget_get_style_context w) >>= \context -> toState (gtk_style_context_remove_class context cls) -setMargins :: !GtkMargins !w -> GtkM w | gtkWidget w -setMargins {left,top,right,bottom} widget = - let (GtkWidget w) = gtkWidget widget in - toState (gtk_widget_set_margin_left w left) >>| - toState (gtk_widget_set_margin_top w top) >>| - toState (gtk_widget_set_margin_right w right) >>| - toState (gtk_widget_set_margin_bottom w bottom) >>| - pure widget +instance tune w GtkCSSClass | gtkWidget w +where + tune (Class cls) widget = + let (GtkWidget w) = gtkWidget widget in + toStateR (gtk_widget_get_style_context w) >>= \context -> + toState (gtk_style_context_add_class context cls) >>| + pure widget + +instance tune w GtkMargins | gtkWidget w +where + tune {left,top,right,bottom} widget = + let (GtkWidget w) = gtkWidget widget in + toState (gtk_widget_set_margin_left w left) >>| + toState (gtk_widget_set_margin_top w top) >>| + toState (gtk_widget_set_margin_right w right) >>| + toState (gtk_widget_set_margin_bottom w bottom) >>| + pure widget instance tune w GtkSensitivity | gtkWidget w where diff --git a/src/Gtk/Widgets/Sheet.dcl b/src/Gtk/Widgets/Sheet.dcl index 2541242..0f365d2 100644 --- a/src/Gtk/Widgets/Sheet.dcl +++ b/src/Gtk/Widgets/Sheet.dcl @@ -4,6 +4,8 @@ definition module Gtk.Widgets.Sheet * This module provides support for GtkSheet; a spreadsheet widget. See * https://fpaquet.github.io/gtksheet/ for more details and installation * instructions. Use the 'Gtk with GtkSheet' environment. + * + * Note that the C library is slightly buggy. */ from StdMaybe import :: Maybe @@ -26,8 +28,19 @@ instance gtkWidget GtkSheet instance gtkContainer GtkSheet newSheet :: !Int !Int !String -> GtkM GtkSheet + +/** + * Freezing a sheet means that the GUI will not be updated; all model updates + * are collected and the GUI is updated in a single step when the sheet is + * unfrozen. This is a wrapper function which freezes the sheet, executes the + * monad, and then unfreezes the sheet again. + */ whileFrozen :: !(GtkM a) !GtkSheet -> GtkM a +/** + * Make sure the sheet has the given width and height by adding or deleting + * rows and columns. + */ ensureDimensions :: !Int !Int !GtkSheet -> GtkM GtkSheet setColumnTitle :: !Int !String !GtkSheet -> GtkM GtkSheet diff --git a/src/Gtk/Widgets/Sheet/Signal.dcl b/src/Gtk/Widgets/Sheet/Signal.dcl index 5708651..9084b4d 100644 --- a/src/Gtk/Widgets/Sheet/Signal.dcl +++ b/src/Gtk/Widgets/Sheet/Signal.dcl @@ -4,11 +4,16 @@ from StdMaybe import :: Maybe from Gtk.Signal import class signalHandler, :: SignalHandlerInternal from Gtk.State import :: GtkM +from Gtk.Tune import class tune +from Gtk.Types import :: GtkPropagate +from Gtk.Widgets import class gtkWidget :: GtkSheetSignalHandler = DeactivateHandler !(Int Int -> GtkM Bool) - | EnterPressedHandler !(GtkM Bool) + | EnterPressedHandler !(GtkM GtkPropagate) | SheetActivateHandler !(Int Int -> GtkM ()) | TraverseHandler !((Maybe (Int,Int)) (Int,Int) -> GtkM (Maybe (Int, Int))) instance signalHandler GtkSheetSignalHandler + +instance tune w GtkSheetSignalHandler | gtkWidget w diff --git a/src/Gtk/Widgets/Sheet/Signal.icl b/src/Gtk/Widgets/Sheet/Signal.icl index 6de4209..d08f4dd 100644 --- a/src/Gtk/Widgets/Sheet/Signal.icl +++ b/src/Gtk/Widgets/Sheet/Signal.icl @@ -18,7 +18,7 @@ where TraverseHandler _ -> "traverse" signalHandler handler = case handler of DeactivateHandler f -> SHI_Int_Int_Bool f - EnterPressedHandler f -> SHI_Pointer_Bool \_ -> not <$> f + EnterPressedHandler f -> SHI_Pointer_Bool \_ -> (\p -> p=:StopPropagation) <$> f SheetActivateHandler f -> SHI_Int_Int_Bool \r c -> f r c >>| pure True TraverseHandler f -> SHI_Int_Int_Pointer_Pointer_Bool \oldrow oldcol newrowp newcolp -> let newrow = readInt4S newrowp 0; newcol = readInt4S newcolp 0 in @@ -35,3 +35,7 @@ where appWorld (forceEval (writeInt4 newrowp 0 row)) >>| appWorld (forceEval (writeInt4 newcolp 0 col)) >>| pure True) + +instance tune w GtkSheetSignalHandler | gtkWidget w +where + tune handler widget = installSignalHandler handler widget |