nano-ui
Copyright(c) 2026 Zachary Churchill
LicenseMIT
Maintainerzacharyachurchill@gmail.com
Safe HaskellNone
LanguageGHC2024

NanoUI

Description

A view is a function that runs every frame. Widgets are ordinary calls: each one lays itself out, reads this frame's input, and returns what the user did. There are no widget objects to keep and no callbacks to register.

counter :: NanoUI ()
counter = do
  (n, setN) <- useInt 0
  row $ do
    whenM (button "-") (setN (n - 1))
    label (T.pack (show n))
    whenM (button "+") (setN (n + 1))

Run a view with a backend: runSdlApp from nano-ui-sdl or runRgfwApp from nano-ui-rgfw.

Conventions

  • Widgets return what you usually need: Bool for buttons and menu items, the new value for inputs, and () for text and decoration.
  • A primed name also returns the widget's Response, for hover state, geometry, tooltips, and change or submit flags: button', slider'.
  • Inputs are controlled. Pass the current value and keep the result; a change you do not store is undone on the next frame. Editing state such as the caret, a drag in progress, or an open dropdown stays inside the widget.
  • Layout arguments are modifiers, as in buttonWith (fixedW 120) or columnWith (gap 8 . padAll 12). Widgets with more options take a configuration record: textInputConfigured, tabsConfigured.

State

Keep state in local hooks (useInt, useText, useState), in a model you pass down through the view, or in a reducer: NanoUI.Emit has widgets that emit messages, and the backends' reducer runners fold them into the model.

Synopsis

Views

type NanoUI = Eff '[Ui, IOE] Source #

data Ui (a :: Type -> Type) b Source #

Instances

Instances details
type DispatchOf Ui Source # 
Instance details

Defined in NanoUI.Monad

data StaticRep Ui Source # 
Instance details

Defined in NanoUI.Monad

runUi :: forall (es :: [Effect]) a. IOE :> es => Context -> Input -> Eff (Ui ': es) a -> Eff es a Source #

uiIO :: forall (es :: [Effect]) a. Ui :> es => IO a -> Eff es a Source #

whenM :: Monad m => m Bool -> m () -> m () Source #

Monadic variant of when. Runs the second action if the first returns True.

Example:

whenM (button Save) saveDocument

unlessM :: Monad m => m Bool -> m () -> m () Source #

Monadic variant of unless. Runs the second action if the first returns False.

ifM :: Monad m => m Bool -> m a -> m a -> m a Source #

Monadic conditional selection.

windowSize :: forall (es :: [Effect]). Ui :> es => Eff es Size Source #

windowWidth :: forall (es :: [Effect]). Ui :> es => Eff es Float Source #

windowHeight :: forall (es :: [Effect]). Ui :> es => Eff es Float Source #

uiMousePos :: forall (es :: [Effect]). Ui :> es => Eff es V2 Source #

Widget identity

Every widget and hook takes the next WidgetId in its container: ids count up in call order among siblings, and a container starts a new count for its children. Widget state is stored under that id, so the same widgets and hooks must run in the same order every frame.

A widget that runs on some frames and not others moves the ids of the siblings after it. Put the conditional part inside scope, which takes one id whether or not its body adds anything. For a list whose items are added, removed or reordered, run each item under withKey (or keyed) with a key unique among its siblings, so the item's state follows its key instead of its position.

scope :: forall (es :: [Effect]) a. Ui :> es => Eff es a -> Eff es a Source #

keyed :: forall k (es :: [Effect]) a. (Hashable k, Ui :> es) => k -> Eff es a -> Eff es a Source #

Stable child path from tag. Keys must be unique among siblings in the same scope.

keyedTag :: forall (es :: [Effect]) a. Ui :> es => Word64 -> Eff es a -> Eff es a Source #

withKey :: forall k (es :: [Effect]) a. (Hashable k, Ui :> es) => k -> Eff es a -> Eff es a Source #

nextId :: forall (es :: [Effect]). Ui :> es => Eff es WidgetId Source #

currentId :: forall (es :: [Effect]). Ui :> es => Eff es WidgetId Source #

The id nextId would issue, without consuming it.

burstNextIds :: forall (es :: [Effect]). Ui :> es => Int -> Eff es () Source #

Issue many widget ids in one IO loop (avoids deep Eff bind chains).

newtype WidgetId Source #

Constructors

WidgetId Word64 

Instances

Instances details
Eq WidgetId Source # 
Instance details

Defined in NanoUI.Id

Ord WidgetId Source # 
Instance details

Defined in NanoUI.Id

Show WidgetId Source # 
Instance details

Defined in NanoUI.Id

Hashable WidgetId Source # 
Instance details

Defined in NanoUI.Id

Methods

hashWithSalt :: Int -> WidgetId -> Int #

hash :: WidgetId -> Int #

Prim WidgetId Source # 
Instance details

Defined in NanoUI.Id

data IdContext Source #

Instances

Instances details
Eq IdContext Source # 
Instance details

Defined in NanoUI.Id

Show IdContext Source # 
Instance details

Defined in NanoUI.Id

Responses

class HasResponse r where Source #

Anything that carries a widget Response (composite widget results such as TabResponse). The resp* accessors work on all of them.

Methods

toResponse :: r -> Response Source #

Instances

Instances details
HasResponse Response Source # 
Instance details

Defined in NanoUI.Widgets.Node

HasResponse TableResponse Source # 
Instance details

Defined in NanoUI.Widgets.Table

HasResponse (TabResponse a) Source # 
Instance details

Defined in NanoUI.Widgets.Tabs

Containers

row :: forall (es :: [Effect]) a. Ui :> es => Eff es a -> Eff es a Source #

rowWith :: forall (es :: [Effect]) a. Ui :> es => (Layout -> Layout) -> Eff es a -> Eff es a Source #

column :: forall (es :: [Effect]) a. Ui :> es => Eff es a -> Eff es a Source #

columnWith :: forall (es :: [Effect]) a. Ui :> es => (Layout -> Layout) -> Eff es a -> Eff es a Source #

hstack :: forall f (es :: [Effect]). (Foldable f, Ui :> es) => f (Eff es ()) -> Eff es () Source #

Run a collection of widgets side by side, as in hstack (map label names).

vstack :: forall f (es :: [Effect]). (Foldable f, Ui :> es) => f (Eff es ()) -> Eff es () Source #

Run a collection of widgets top to bottom.

grid :: forall (es :: [Effect]) a. Ui :> es => Int -> Eff es a -> Eff es a Source #

gridWith :: forall (es :: [Effect]) a. Ui :> es => Int -> (Layout -> Layout) -> Eff es a -> Eff es a Source #

panel :: forall (es :: [Effect]) a. Ui :> es => Eff es a -> Eff es a Source #

panelWith :: forall (es :: [Effect]) a. Ui :> es => (Layout -> Layout) -> Eff es a -> Eff es a Source #

card :: forall (es :: [Effect]) a. Ui :> es => Eff es a -> Eff es a Source #

callout :: forall (es :: [Effect]) a. Ui :> es => Color -> Eff es a -> Eff es a Source #

calloutWith :: forall (es :: [Effect]) a. Ui :> es => Color -> (Layout -> Layout) -> Eff es a -> Eff es a Source #

A panel tinted with col: a border in it and a faint wash of it over the panel colour. The tint applies to the callout's own panel and to panels nested in it.

toolbar :: forall (es :: [Effect]) a. Ui :> es => Eff es a -> Eff es a Source #

center :: forall (es :: [Effect]) a. Ui :> es => Eff es a -> Eff es a Source #

responsive :: forall (es :: [Effect]) a. Ui :> es => Float -> (Eff es a -> Eff es a) -> (Eff es a -> Eff es a) -> Eff es a -> Eff es a Source #

Choose between two container builders based on window width.

responsiveRowCol :: forall (es :: [Effect]) a. Ui :> es => Float -> (Layout -> Layout) -> Eff es a -> Eff es a Source #

A row while the window is at least breakpoint wide, a column below it.

scroll :: forall (es :: [Effect]) a. Ui :> es => Eff es a -> Eff es a Source #

scrollWith :: forall (es :: [Effect]) a. Ui :> es => (Layout -> Layout) -> Eff es a -> Eff es a Source #

scroll2D :: forall (es :: [Effect]) a. Ui :> es => Eff es a -> Eff es a Source #

Scroll container on both axes.

scroll2DWith :: forall (es :: [Effect]) a. Ui :> es => (Layout -> Layout) -> Eff es a -> Eff es a Source #

scrollArea :: forall (es :: [Effect]) a. Ui :> es => (Layout -> Layout) -> Eff es a -> Eff es (WidgetId, a) Source #

scrollWith that also returns the container's widget id, which keys its scroll offset.

scrollArea2D :: forall (es :: [Effect]) a. Ui :> es => (Layout -> Layout) -> Eff es a -> Eff es (WidgetId, a) Source #

scroll2DWith that also returns the container's widget id.

separator :: forall (es :: [Effect]). Ui :> es => Eff es () Source #

A one-pixel rule: horizontal in a column, vertical in a row.

spacer :: forall (es :: [Effect]). Ui :> es => Sizing -> Sizing -> Eff es () Source #

Empty space with the given sizing on each axis.

flex :: forall (es :: [Effect]). Ui :> es => Eff es () Source #

Takes up the remaining space along the parent's direction.

Text

label :: forall (es :: [Effect]). Ui :> es => Text -> Eff es () Source #

A line of text. Newlines start new lines.

label' :: forall (es :: [Effect]). Ui :> es => Text -> Eff es Response Source #

label returning its Response, for a tooltip or an anchored popup.

labelWith :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Text -> Eff es () Source #

label with a layout modifier, for example labelWith fontMono.

labelWith' :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Text -> Eff es Response Source #

heading :: forall (es :: [Effect]). Ui :> es => Text -> Eff es () Source #

muted :: forall (es :: [Effect]). Ui :> es => Text -> Eff es () Source #

mono :: forall (es :: [Effect]). Ui :> es => Text -> Eff es () Source #

danger :: forall (es :: [Effect]). Ui :> es => Text -> Eff es () Source #

bold :: forall (es :: [Effect]). Ui :> es => Text -> Eff es () Source #

italic :: forall (es :: [Effect]). Ui :> es => Text -> Eff es () Source #

underline :: forall (es :: [Effect]). Ui :> es => Text -> Eff es () Source #

kv :: forall (es :: [Effect]). Ui :> es => Text -> Text -> Eff es () Source #

Key/value row: a muted key on the left, the value right-aligned. Trailing whitespace in the value is dropped.

kvMono :: forall (es :: [Effect]). Ui :> es => Text -> Text -> Eff es () Source #

Key/value row with a monospace value.

kvBlock :: forall f (es :: [Effect]). (Foldable f, Ui :> es) => f (Text, Text) -> Eff es () Source #

Key/value pairs as one monospace block with the keys padded to a column.

selectableText :: forall (es :: [Effect]). Ui :> es => Text -> Eff es () Source #

Read-only text that can be selected with the mouse and copied with Ctrl+C.

selectableText' :: forall (es :: [Effect]). Ui :> es => Text -> Eff es Response Source #

selectableTextWith :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Text -> Eff es () Source #

selectableTextWith' :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Text -> Eff es Response Source #

Buttons and menus

button :: forall (es :: [Effect]). Ui :> es => Text -> Eff es Bool Source #

Button with a text label. True on the frame it is clicked, by pointer or by Enter or Space while focused.

whenM (button Save) saveDocument

button' :: forall (es :: [Effect]). Ui :> es => Text -> Eff es Response Source #

button returning its Response, for tooltips, anchored popups, or hover state.

help <- button' Help
tooltip help "Open the manual"
when (respClicked help) openManual

buttonWith :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Text -> Eff es Bool Source #

button with a layout modifier.

whenM (buttonWith (fixedW 120) Submit) submitForm

buttonWith' :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Text -> Eff es Response Source #

menuButton :: forall (es :: [Effect]). Ui :> es => Text -> Bool -> Eff es Bool Source #

Menu-bar title: a flat, label-sized button. open tints the title while its drop-down is showing. True on the frame it is clicked.

menuButton' :: forall (es :: [Effect]). Ui :> es => Text -> Bool -> Eff es Response Source #

menuButton returning its Response, whose rect anchors the drop-down.

menuItem :: forall (es :: [Effect]). Ui :> es => Text -> Eff es Bool Source #

Menu row. True on the frame it is clicked.

whenM (menuItem "Open...") openFile

menuItem' :: forall (es :: [Effect]). Ui :> es => Text -> Eff es Response Source #

menuItemShortcut :: forall (es :: [Effect]). Ui :> es => Text -> Text -> Eff es Bool Source #

Menu row with a shortcut hint after the label. The hint is only text; handle the key itself elsewhere.

whenM (menuItemShortcut Save "Ctrl+S") saveFile

menuItemDisabled :: forall (es :: [Effect]). Ui :> es => Text -> Eff es () Source #

Dimmed menu row that cannot be clicked.

menuSeparator :: forall (es :: [Effect]). Ui :> es => Eff es () Source #

Separator line inside a context menu, matching the text-field context menu painter exactly: a 1px rule inset menuItemPadX from the panel edge (the popup already contributes menuOuterPad, the row adds the remainder) centered in a menuSepH band (lineY = bandY + h/2 via 4.5px vertical padding around a zero-height content box). The rule sits in a tight column so it stays horizontal (separator adapts to its parent's direction and would grow vertically inside the padded row) and so the default 3px container padding does not inset or stretch it.

menuHeader :: forall (es :: [Effect]). Ui :> es => Text -> Eff es () Source #

Header / category title inside a context menu.

contextMenu :: forall (es :: [Effect]) r a. (Ui :> es, HasResponse r) => r -> Eff es a -> Eff es (Maybe a) Source #

A context menu for any widget response, opened by right-clicking it. Returns the menu body's result while the menu is open.

contextMenuArea :: forall (es :: [Effect]) a b. Ui :> es => (Layout -> Layout) -> Eff es a -> (V2 -> Eff es b) -> Eff es (a, Maybe b) Source #

A container whose right-click opens a context menu. The menu body receives the position it was opened at.

useContextMenu :: forall (es :: [Effect]). Ui :> es => Eff es (Bool, V2, V2 -> Eff es (), Eff es ()) Source #

Open state for a context menu you position yourself: whether it is open, where it was opened, an action to open it at a point, and one to close it.

Inputs

checkbox :: forall (es :: [Effect]). Ui :> es => Text -> Bool -> Eff es Bool Source #

Checkbox with a caption. Pass whether it is checked; the result is the state after this frame's click or Space/Enter.

checkbox' :: forall (es :: [Effect]). Ui :> es => Text -> Bool -> Eff es (Response, Bool) Source #

toggleSwitch :: forall (es :: [Effect]). Ui :> es => Bool -> Eff es Bool Source #

On/off switch. Pass the current state; the result is the state after this frame's click or Space/Enter.

toggleSwitch' :: forall (es :: [Effect]). Ui :> es => Bool -> Eff es (Response, Bool) Source #

toggleSwitchWith :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Bool -> Eff es Bool Source #

toggleSwitch with a layout modifier.

toggleSwitchWith' :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Bool -> Eff es (Response, Bool) Source #

radio :: forall f (es :: [Effect]). (Foldable f, Ui :> es) => f Text -> Int -> Eff es Int Source #

A column of radio buttons over options in fold order. Pass the selected index; the result is the index after this frame's click or arrow keys.

radio' :: forall f (es :: [Effect]). (Foldable f, Ui :> es) => f Text -> Int -> Eff es (Response, Int) Source #

boundedRadio :: forall a (es :: [Effect]). (Bounded a, Enum a, Ui :> es) => (a -> Text) -> a -> Eff es a Source #

Radio buttons for every value of a bounded enum, labelled by encode.

boundedRadio' :: forall a (es :: [Effect]). (Bounded a, Enum a, Ui :> es) => (a -> Text) -> a -> Eff es (Response, a) Source #

enumRadio :: forall a (es :: [Effect]). (Bounded a, Enum a, Show a, Ui :> es) => a -> Eff es a Source #

boundedRadio labelled with show.

enumRadio' :: forall a (es :: [Effect]). (Bounded a, Enum a, Show a, Ui :> es) => a -> Eff es (Response, a) Source #

select :: forall f (es :: [Effect]). (Foldable f, Ui :> es) => f Text -> Int -> Eff es Int Source #

Dropdown over options in fold order. Pass the selected index; the result is the index after this frame's pick.

select' :: forall f (es :: [Effect]). (Foldable f, Ui :> es) => f Text -> Int -> Eff es (Response, Int) Source #

selectWith :: forall f (es :: [Effect]). (Foldable f, Ui :> es) => (Layout -> Layout) -> f Text -> Int -> Eff es Int Source #

select with a layout modifier.

selectWith' :: forall f (es :: [Effect]). (Foldable f, Ui :> es) => (Layout -> Layout) -> f Text -> Int -> Eff es (Response, Int) Source #

boundedSelect :: forall a (es :: [Effect]). (Bounded a, Enum a, Ui :> es) => (a -> Text) -> a -> Eff es a Source #

Select over every value of a bounded enum, labelled by encode.

boundedSelect' :: forall a (es :: [Effect]). (Bounded a, Enum a, Ui :> es) => (a -> Text) -> a -> Eff es (Response, a) Source #

enumSelect :: forall a (es :: [Effect]). (Bounded a, Enum a, Show a, Ui :> es) => a -> Eff es a Source #

boundedSelect labelled with show.

enumSelect' :: forall a (es :: [Effect]). (Bounded a, Enum a, Show a, Ui :> es) => a -> Eff es (Response, a) Source #

slider :: forall (es :: [Effect]). Ui :> es => Float -> Float -> Float -> Eff es Float Source #

Slider over [minV, maxV] that fills the available width. Pass the current value; the result is the value after this frame's drag or arrow keys.

slider' :: forall (es :: [Effect]). Ui :> es => Float -> Float -> Float -> Eff es (Response, Float) Source #

sliderWith :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Float -> Float -> Float -> Eff es Float Source #

slider with a layout modifier.

volume' <- sliderWith (fixedW 200) 0 100 volume

sliderWith' :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Float -> Float -> Float -> Eff es (Response, Float) Source #

knob :: forall (es :: [Effect]). Ui :> es => Float -> Float -> Float -> Eff es Float Source #

Rotary knob over [minV, maxV], 36 px across. Drag vertically, scroll, or use the arrow keys. Pass the current value; the result is the value after this frame.

knob' :: forall (es :: [Effect]). Ui :> es => Float -> Float -> Float -> Eff es (Response, Float) Source #

knobWith :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Float -> Float -> Float -> Float -> Eff es Float Source #

knob with a layout modifier and a diameter in pixels.

knobWith' :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Float -> Float -> Float -> Float -> Eff es (Response, Float) Source #

textInput :: forall (es :: [Effect]). Ui :> es => Text -> Eff es Text Source #

Single-line text field. Pass the current text; the result is the text after this frame's typing, pastes, and menu edits.

textInput' :: forall (es :: [Effect]). Ui :> es => Text -> Eff es (Response, Text) Source #

textInputConfigured :: forall (es :: [Effect]). Ui :> es => TextInputConfig -> Text -> Eff es Text Source #

textInput with a placeholder, password masking, or its own layout.

secret' <- textInputConfigured defaultTextInputConfig {ticPassword = True} secret

textInputConfigured' :: forall (es :: [Effect]). Ui :> es => TextInputConfig -> Text -> Eff es (Response, Text) Source #

data NumericInputConfig Source #

How a numeric field reads, shows, and steps its value.

Constructors

NumericInputConfig 

Fields

numericInput :: forall (es :: [Effect]). Ui :> es => Double -> Eff es Double Source #

Numeric field over whole numbers. Pass the current value; the result is the value after this frame's typing, arrow keys, and stepper clicks.

Only digits, and a leading minus sign when the range reaches below zero, can be typed. Up and Down step the value, Shift steps ten times as far, and holding a stepper arrow repeats. Enter, a step, or leaving the field rewrites the text as the value, clamped to the range.

numericInput' :: forall (es :: [Effect]). Ui :> es => Double -> Eff es (Response, Double) Source #

numericInputConfigured :: forall (es :: [Effect]). Ui :> es => NumericInputConfig -> Double -> Eff es Double Source #

numericInput with a range, a step, decimal places, hexadecimal mode, or its own layout.

byte' <- numericInputConfigured defaultNumericInputConfig {nicMin = 0, nicMax = 255, nicHex = True} byte

data SearchFieldConfig Source #

Search field: a caption-less NodeTextInput with an embedded magnifier and clear button. The label acts as the placeholder. Change pulses are debounced (trailing edge); clearing with the embedded button fires immediately.

searchField :: forall (es :: [Effect]). Ui :> es => Text -> Text -> Eff es Text Source #

Search box with a magnifier and a clear button; the first argument is the placeholder. Pass the current text; the result is the text after this frame. respChanged on searchField' is debounced: it fires once typing pauses, or at once when the field is cleared.

searchField' :: forall (es :: [Effect]). Ui :> es => Text -> Text -> Eff es (Response, Text) Source #

searchFieldConfigured :: forall (es :: [Effect]). Ui :> es => SearchFieldConfig -> Text -> Eff es Text Source #

comboBox :: forall f (es :: [Effect]). (Foldable f, Ui :> es) => Text -> f Text -> Text -> Eff es Text Source #

Combo box: the searchField with a select-style dropdown of options. While the field holds focus, the shared select dropdown overlay lists the options filtered by the field text (all of them while it is empty). The value is free text: options are suggestions, not a closed set. See comboStep for when the value commits. Pass the current text; the result is the text after this frame, and respChanged on comboBox' marks a commit.

comboBox' :: forall f (es :: [Effect]). (Foldable f, Ui :> es) => Text -> f Text -> Text -> Eff es (Response, Text) Source #

textArea :: forall (es :: [Effect]). Ui :> es => Text -> Eff es Text Source #

Multi-line text editor. Pass the current text; the result is the text after this frame's edits. Pair it with a label when a caption is wanted.

textArea' :: forall (es :: [Effect]). Ui :> es => Text -> Eff es (Response, Text) Source #

textAreaWith :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Text -> Eff es Text Source #

textArea with a modifier applied to textAreaLayout, for example grow to fill the parent.

textAreaWith' :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Text -> Eff es (Response, Text) Source #

colorPicker :: forall (es :: [Effect]). Ui :> es => Color -> Eff es Color Source #

RGB colour picker: a saturation/value field, a hue bar, and RGB, HSV and hex fields. Pass the current colour; the result is the colour after this frame's edits.

The field and each bar take keyboard focus in turn. On the field the arrow keys move the marker (left and right for saturation, up and down for value); on a bar they move its handle, and Home and End jump to its ends. Shift takes steps ten times larger.

colorPicker' :: forall (es :: [Effect]). Ui :> es => Color -> Eff es (Response, Color) Source #

colorPickerRGBA :: forall (es :: [Effect]). Ui :> es => Color -> Eff es Color Source #

colorPicker with an alpha bar and an A / #RRGGBBAA field.

colorPickerRGBA' :: forall (es :: [Effect]). Ui :> es => Color -> Eff es (Response, Color) Source #

colorToHexA :: Color -> Text Source #

Eight-digit form for the alpha-aware picker: #RRGGBBAA.

Text editing

Text fields change their text only through TextCommands. Keys run them (Backspace is Delete CharLeft, Ctrl+Z is Undo), the right-click menu runs them, and an app can run them on a field by its id:

(resp, body') <- textArea' body
canUndo <- textCanUndo (respId resp)
whenM (menuItem "Undo") (runTextCommand (respId resp) Undo)
whenM (menuItem "Insert date") (runTextCommand (respId resp) (InsertText today))

Every command that changes text is recorded for undo. Typing joins one undo step per word and deleting one per run; the steps keep the edits themselves, not copies of the document, so a long history of a large document stays small. Replacing the value a field is passed clears its history.

data TextCommand Source #

Something done to a text field. Commands that change text are undoable and replace the selection where one exists.

Constructors

InsertText !Text

Replace the selection with text (typing, a snippet).

Delete !TextMotion

Delete the selection, or from the cursor to where the motion lands: Delete CharLeft is Backspace, Delete WordRight Ctrl+Delete.

Move !TextMotion !Bool

Move the cursor, extending the selection when the flag is set.

SelectAll 
Select !Cursor !Cursor

Select from the first position (the anchor) to the second (the cursor), clamped into the document.

Replace !Cursor !Cursor !Text

Replace the text between two positions, leaving the cursor after it.

ReplaceAll !Text

Replace the whole document as one undoable edit.

Undo 
Redo 
Cut 
Copy 
Paste 

Instances

Instances details
Eq TextCommand Source # 
Instance details

Defined in NanoUI.Widgets.TextCommand

Show TextCommand Source # 
Instance details

Defined in NanoUI.Widgets.TextCommand

data Cursor Source #

Zero-indexed logical (row, column) position in the buffer. Fields are row then column, so the derived Ord is document order.

Constructors

Cursor 

Fields

Instances

Instances details
Eq Cursor Source # 
Instance details

Defined in NanoUI.Widgets.TextBuffer

Methods

(==) :: Cursor -> Cursor -> Bool #

(/=) :: Cursor -> Cursor -> Bool #

Ord Cursor Source # 
Instance details

Defined in NanoUI.Widgets.TextBuffer

Show Cursor Source # 
Instance details

Defined in NanoUI.Widgets.TextBuffer

runTextCommand :: forall (es :: [Effect]). Ui :> es => WidgetId -> TextCommand -> Eff es () Source #

Run a command on the text field (text input, search field, text area) with this id, as if its keys were pressed: runTextCommand (respId resp) Undo. The field takes keyboard focus, and its next frame returns the changed text and a respChanged pulse. An id that is not a text field is ignored.

textCanUndo :: forall (es :: [Effect]). Ui :> es => WidgetId -> Eff es Bool Source #

Whether Undo would change the field, for enabling a menu item.

textCanRedo :: forall (es :: [Effect]). Ui :> es => WidgetId -> Eff es Bool Source #

Whether Redo would change the field, for enabling a menu item.

Tabs, trees, and tables

data Tab a body Source #

Constructors

Tab 

Fields

data TabResponse a Source #

Constructors

TabResponse 

Fields

Instances

Instances details
Eq a => Eq (TabResponse a) Source # 
Instance details

Defined in NanoUI.Widgets.Tabs

Show a => Show (TabResponse a) Source # 
Instance details

Defined in NanoUI.Widgets.Tabs

HasResponse (TabResponse a) Source # 
Instance details

Defined in NanoUI.Widgets.Tabs

data TabsConfig Source #

Header look and placement for tabsConfigured and tabBarConfigured.

Instances

Instances details
Eq TabsConfig Source # 
Instance details

Defined in NanoUI.Widgets.Tabs

Show TabsConfig Source # 
Instance details

Defined in NanoUI.Widgets.Tabs

defaultTabsConfig :: TabsConfig Source #

Underlined headers along the top.

tab :: a -> Text -> body -> Tab a body Source #

closableTab :: a -> Text -> body -> Tab a body Source #

tabs :: forall f a (es :: [Effect]). (Foldable f, Eq a, Ui :> es) => a -> f (Tab a (Eff es ())) -> Eff es a Source #

Tab headers and the active tab's body. Pass the active key; the result is the active key after this frame's clicks or arrow keys. Only the active tab's body runs.

tabs' :: forall f a (es :: [Effect]). (Foldable f, Eq a, Ui :> es) => a -> f (Tab a (Eff es ())) -> Eff es (TabResponse a) Source #

tabs returning the TabResponse, which also reports a closed tab.

tabsConfigured :: forall f a (es :: [Effect]). (Foldable f, Eq a, Ui :> es) => TabsConfig -> a -> f (Tab a (Eff es ())) -> Eff es a Source #

tabs with a header style and placement.

tabsConfigured' :: forall f a (es :: [Effect]). (Foldable f, Eq a, Ui :> es) => TabsConfig -> a -> f (Tab a (Eff es ())) -> Eff es (TabResponse a) Source #

tabBar :: forall f a (es :: [Effect]) body. (Foldable f, Eq a, Ui :> es) => a -> f (Tab a body) -> Eff es a Source #

Tab headers only; the caller renders the body.

tabBar' :: forall f a (es :: [Effect]) body. (Foldable f, Eq a, Ui :> es) => a -> f (Tab a body) -> Eff es (TabResponse a) Source #

tabBarConfigured :: forall f a (es :: [Effect]) body. (Foldable f, Eq a, Ui :> es) => TabsConfig -> a -> f (Tab a body) -> Eff es a Source #

tabBarConfigured' :: forall f a (es :: [Effect]) body. (Foldable f, Eq a, Ui :> es) => TabsConfig -> a -> f (Tab a body) -> Eff es (TabResponse a) Source #

data TreeItem Source #

Constructors

TreeItem 

Instances

Instances details
Eq TreeItem Source # 
Instance details

Defined in NanoUI.Widgets.Tree

Show TreeItem Source # 
Instance details

Defined in NanoUI.Widgets.Tree

tree :: forall f (es :: [Effect]). (Foldable f, Ui :> es) => Text -> f TreeItem -> Int -> Eff es Int Source #

Collapsible tree. Rows are numbered in pre-order; pass the selected row and the result is the selection after this frame's click or arrow keys. Expansion is kept by the widget. key distinguishes trees in one scope.

tree' :: forall f (es :: [Effect]). (Foldable f, Ui :> es) => Text -> f TreeItem -> Int -> Eff es (Response, Int) Source #

data SortDir Source #

Constructors

SortAsc 
SortDesc 

Instances

Instances details
Eq SortDir Source # 
Instance details

Defined in NanoUI.Widgets.Table

Methods

(==) :: SortDir -> SortDir -> Bool #

(/=) :: SortDir -> SortDir -> Bool #

Bounded SortDir Source # 
Instance details

Defined in NanoUI.Widgets.Table

Enum SortDir Source # 
Instance details

Defined in NanoUI.Widgets.Table

Show SortDir Source # 
Instance details

Defined in NanoUI.Widgets.Table

data SortCol Source #

Constructors

SortCol 

Instances

Instances details
Eq SortCol Source # 
Instance details

Defined in NanoUI.Widgets.Table

Methods

(==) :: SortCol -> SortCol -> Bool #

(/=) :: SortCol -> SortCol -> Bool #

Show SortCol Source # 
Instance details

Defined in NanoUI.Widgets.Table

data ColSize Source #

Instances

Instances details
Eq ColSize Source # 
Instance details

Defined in NanoUI.Widgets.Table

Methods

(==) :: ColSize -> ColSize -> Bool #

(/=) :: ColSize -> ColSize -> Bool #

Show ColSize Source # 
Instance details

Defined in NanoUI.Widgets.Table

data TableConfig Source #

Instances

Instances details
Eq TableConfig Source # 
Instance details

Defined in NanoUI.Widgets.Table

Show TableConfig Source # 
Instance details

Defined in NanoUI.Widgets.Table

table :: forall f (es :: [Effect]) row. (Foldable f, Ui :> es) => Text -> Colonnade Headed row Text -> f row -> SortCol -> Eff es TableResponse Source #

Sortable table with resizable, reorderable columns. key tells tables in one scope apart, and the columns are a colonnade over row. Pass the current sort; the TableResponse carries the sort after this frame's header clicks, along with the column order and hidden columns.

tableWith :: forall f (es :: [Effect]) row. (Foldable f, Ui :> es) => (Layout -> Layout) -> Text -> Colonnade Headed row Text -> f row -> SortCol -> Eff es TableResponse Source #

table with a layout modifier.

tableConfigured :: forall f (es :: [Effect]) row. (Foldable f, Ui :> es) => TableConfig -> (Layout -> Layout) -> Text -> Colonnade Headed row Text -> f row -> SortCol -> Eff es TableResponse Source #

tableWith with column sizes, frozen rows and columns, and initially hidden columns.

simpleTable :: forall f (es :: [Effect]). (Foldable f, Ui :> es) => [Text] -> f [Text] -> Eff es TableResponse Source #

A table of text rows under the given headers.

useTableSort :: forall (es :: [Effect]). Ui :> es => SortCol -> Eff es (SortCol, SortCol -> Eff es ()) Source #

sortRows :: Foldable f => Colonnade Headed row Text -> SortCol -> f row -> [row] Source #

data Colonnade (h :: Type -> Type) a c #

An columnar encoding of a. The type variable h determines what is present in each column in the header row. It is typically instantiated to Headed and occasionally to Headless. There is nothing that restricts it to these two types, although they satisfy the majority of use cases. The type variable c is the content type. This can be Text, String, or ByteString. In the companion libraries reflex-dom-colonnade and yesod-colonnade, additional types that represent HTML with element attributes are provided that serve as the content type. Presented more visually:

            +---- Value consumed to build a row
            |
            v
Colonnade h a c
          ^   ^
          |   |
          |   +-- Content (Text, ByteString, Html, etc.)
          |
          +------ Headedness (Headed or Headless)

Internally, a Colonnade is represented as a Vector of individual column encodings. It is possible to use any collection type with Alternative and Foldable instances. However, Vector was chosen to optimize the data structure for the use case of building the structure once and then folding over it many times. It is recommended that Colonnades are defined at the top-level so that GHC avoids reconstructing them every time they are used.

Instances

Instances details
Functor h => Profunctor (Colonnade h) # 
Instance details

Defined in Colonnade.Encode

Methods

dimap :: (a -> b) -> (c -> d) -> Colonnade h b c -> Colonnade h a d #

lmap :: (a -> b) -> Colonnade h b c -> Colonnade h a c #

rmap :: (b -> c) -> Colonnade h a b -> Colonnade h a c #

(#.) :: forall a b c q. Coercible c b => q b c -> Colonnade h a b -> Colonnade h a c #

(.#) :: forall a b c q. Coercible b a => Colonnade h b c -> q a b -> Colonnade h a c #

Functor h => Functor (Colonnade h a) # 
Instance details

Defined in Colonnade.Encode

Methods

fmap :: (a0 -> b) -> Colonnade h a a0 -> Colonnade h a b #

(<$) :: a0 -> Colonnade h a b -> Colonnade h a a0 #

Monoid (Colonnade h a c) # 
Instance details

Defined in Colonnade.Encode

Methods

mempty :: Colonnade h a c #

mappend :: Colonnade h a c -> Colonnade h a c -> Colonnade h a c #

mconcat :: [Colonnade h a c] -> Colonnade h a c #

Semigroup (Colonnade h a c) # 
Instance details

Defined in Colonnade.Encode

Methods

(<>) :: Colonnade h a c -> Colonnade h a c -> Colonnade h a c #

sconcat :: NonEmpty (Colonnade h a c) -> Colonnade h a c #

stimes :: Integral b => b -> Colonnade h a c -> Colonnade h a c #

newtype Headed a #

As the first argument to the Colonnade type constructor, this indictates that the columnar encoding has a header. This type is isomorphic to Identity but is given a new name to clarify its intent:

example :: Colonnade Headed Foo Text

The term example represents a columnar encoding of Foo in which the columns have headings.

Constructors

Headed 

Fields

Instances

Instances details
Headedness Headed # 
Instance details

Defined in Colonnade.Encode

Applicative Headed # 
Instance details

Defined in Colonnade.Encode

Methods

pure :: a -> Headed a #

(<*>) :: Headed (a -> b) -> Headed a -> Headed b #

liftA2 :: (a -> b -> c) -> Headed a -> Headed b -> Headed c #

(*>) :: Headed a -> Headed b -> Headed b #

(<*) :: Headed a -> Headed b -> Headed a #

Functor Headed # 
Instance details

Defined in Colonnade.Encode

Methods

fmap :: (a -> b) -> Headed a -> Headed b #

(<$) :: a -> Headed b -> Headed a #

Foldable Headed # 
Instance details

Defined in Colonnade.Encode

Methods

fold :: Monoid m => Headed m -> m #

foldMap :: Monoid m => (a -> m) -> Headed a -> m #

foldMap' :: Monoid m => (a -> m) -> Headed a -> m #

foldr :: (a -> b -> b) -> b -> Headed a -> b #

foldr' :: (a -> b -> b) -> b -> Headed a -> b #

foldl :: (b -> a -> b) -> b -> Headed a -> b #

foldl' :: (b -> a -> b) -> b -> Headed a -> b #

foldr1 :: (a -> a -> a) -> Headed a -> a #

foldl1 :: (a -> a -> a) -> Headed a -> a #

toList :: Headed a -> [a] #

null :: Headed a -> Bool #

length :: Headed a -> Int #

elem :: Eq a => a -> Headed a -> Bool #

maximum :: Ord a => Headed a -> a #

minimum :: Ord a => Headed a -> a #

sum :: Num a => Headed a -> a #

product :: Num a => Headed a -> a #

Eq a => Eq (Headed a) # 
Instance details

Defined in Colonnade.Encode

Methods

(==) :: Headed a -> Headed a -> Bool #

(/=) :: Headed a -> Headed a -> Bool #

Ord a => Ord (Headed a) # 
Instance details

Defined in Colonnade.Encode

Methods

compare :: Headed a -> Headed a -> Ordering #

(<) :: Headed a -> Headed a -> Bool #

(<=) :: Headed a -> Headed a -> Bool #

(>) :: Headed a -> Headed a -> Bool #

(>=) :: Headed a -> Headed a -> Bool #

max :: Headed a -> Headed a -> Headed a #

min :: Headed a -> Headed a -> Headed a #

Read a => Read (Headed a) # 
Instance details

Defined in Colonnade.Encode

Show a => Show (Headed a) # 
Instance details

Defined in Colonnade.Encode

Methods

showsPrec :: Int -> Headed a -> ShowS #

show :: Headed a -> String #

showList :: [Headed a] -> ShowS #

headed :: c -> (a -> c) -> Colonnade Headed a c #

A single column with a header.

headless :: (a -> c) -> Colonnade Headless a c #

A single column without a header.

Overlays

modal :: forall (es :: [Effect]) a. Ui :> es => Bool -> Text -> Eff es a -> Eff es (Response, Maybe a) Source #

window :: forall (es :: [Effect]) a. Ui :> es => Bool -> Text -> Eff es a -> Eff es (Response, Maybe a) Source #

data PopupAnchor Source #

Constructors

AnchorPoint !V2 
AnchorRect !Rect 

Instances

Instances details
Eq PopupAnchor Source # 
Instance details

Defined in NanoUI.Types

Show PopupAnchor Source # 
Instance details

Defined in NanoUI.Types

data PopupConfig Source #

Instances

Instances details
Eq PopupConfig Source # 
Instance details

Defined in NanoUI.Widgets.Popup

Show PopupConfig Source # 
Instance details

Defined in NanoUI.Widgets.Popup

popup :: forall (es :: [Effect]) a. Ui :> es => Bool -> PopupConfig -> Eff es a -> Eff es (Response, Maybe a) Source #

A floating panel placed by the config, shown while open. Returns the body's result while open. The Response reports a dismissal (Escape, or a click outside when cfgDismissable) as a click.

popupWith :: forall (es :: [Effect]) a. Ui :> es => Bool -> PopupConfig -> (Layout -> Layout) -> Eff es a -> Eff es (Response, Maybe a) Source #

popup with a modifier applied to its tight default layout.

tooltip :: forall (es :: [Effect]) r. (Ui :> es, HasResponse r) => r -> Text -> Eff es () Source #

Text shown below a widget while the pointer is over it.

save <- button' Save
tooltip save "Write the file to disk"

tooltipAt :: forall (es :: [Effect]) r. (Ui :> es, HasResponse r) => PopupPlacement -> r -> Text -> Eff es () Source #

tooltip with a placement.

tooltipWidget :: forall (es :: [Effect]) r a. (Ui :> es, HasResponse r) => r -> Eff es a -> Eff es (Maybe a) Source #

Attach a rich tooltip widget to any target response, displayed on hover.

withTooltip :: forall (es :: [Effect]) a b. Ui :> es => Eff es a -> Eff es b -> Eff es (a, Maybe b) Source #

Attach a rich tooltip widget to an inner UI computation.

Pane grids

data PaneGridConfig (es :: [Effect]) Source #

Configuration for a pane grid. pgViewPane can run arbitrary widget code, so the config carries the caller's effect row.

Constructors

PaneGridConfig 

Fields

  • pgLayout :: !(Layout -> Layout)

    Layout modifier for the grid container (default id); pass fillW . fillH to fill the parent area.

  • pgSpacing :: !Float

    Gutter between panes per split level (default 4).

  • pgMinSize :: !Float

    Minimum physical size any pane may shrink to (default 40).

  • pgLeeway :: !Float

    Extra grab margin on each side of a divider, added to pgSpacing to form the divider's real layout gutter. The resize cursor and grab work anywhere in that gutter while only pgSpacing is drawn crisp, so the interaction space is far wider than the visible line (default 6).

  • pgEdgeBand :: !Float

    Thickness of the grid's outer edge that acts as a top-level drop zone (default 20). Dragging a pane into this band restructures the whole grid instead of a single pane: the tree is wrapped in a new top-level split with the dragged pane on that side.

  • pgViewPane :: !(Word64 -> PaneGridCtx es -> Eff es PaneView)

    Renders the content of one pane.

data PaneGridCtx (es :: [Effect]) Source #

Actions handed to a pane so it can mutate the grid immediately.

Constructors

PaneGridCtx 

Fields

data PaneView Source #

What a pane renders to this frame. The pane's content (including any title bar / header) is drawn entirely by the caller in pgViewPane; a header is purely optional and nothing here depends on one existing.

Constructors

PaneView 

Fields

  • pvTitle :: !Text

    Label shown (abbreviated to fit) on the compact drag indicator.

  • pvDraggable :: !Bool

    Grab the pane anywhere inside its own region to drag-and-drop it. This is the easy way to reorder panes without drawing a dedicated handle. Interactive children keep their pointer presses. Pane still needs a drag only on a sub-region? see pvDragPick.

  • pvDragPick :: !(Maybe Rect)

    Optional absolute sub-region (e.g. just a title bar; position it via pgcRect) that also starts a drag. Both handles combine: the pane drags if the press lands in this rect or (when pvDraggable) anywhere in the pane. Nothing here and pvDraggable False makes the pane immovable.

Instances

Instances details
Eq PaneView Source # 
Instance details

Defined in NanoUI.Widgets.PaneGrid

Show PaneView Source # 
Instance details

Defined in NanoUI.Widgets.PaneGrid

data PaneGridResponse Source #

Outcome of one frame of the grid. The pane list, focus, and maximize fields report the state after this pass: actions run by pane content (pgcSplit, pgcClose, ...) and the keyboard handling below take effect in these values and from the next frame's layout onward.

Constructors

PaneGridResponse 

Fields

data GridAxis Source #

Divider orientation. AxisV draws a vertical divider (panes left/right), AxisH draws a horizontal divider (panes stacked top/bottom).

Constructors

AxisV 
AxisH 

paneGrid :: forall (es :: [Effect]). Ui :> es => PaneGridConfig es -> Eff es PaneGridResponse Source #

Progress and sparklines

progressBar :: forall (es :: [Effect]). Ui :> es => Float -> Eff es () Source #

Horizontal progress bar for a fraction in [0, 1]. It fills the available width at a fixed height.

progressBar' :: forall (es :: [Effect]). Ui :> es => Float -> Eff es Response Source #

progressBarWith :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Float -> Float -> Eff es () Source #

progressBar with a layout modifier and a bar height in pixels.

progressBarWith' :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Float -> Float -> Eff es Response Source #

circularProgress :: forall (es :: [Effect]). Ui :> es => Float -> Eff es () Source #

Progress ring for a fraction in [0, 1], 32 px across.

circularProgress' :: forall (es :: [Effect]). Ui :> es => Float -> Eff es Response Source #

circularProgressWith :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Float -> Float -> Eff es () Source #

circularProgress with a layout modifier and a diameter in pixels.

circularProgressWith' :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Float -> Float -> Eff es Response Source #

spinner :: forall (es :: [Effect]). Ui :> es => Eff es () Source #

An indeterminate loading indicator: a short accent arc turning over a faint ring, 18 px across. It keeps the frame loop running while it is on screen and repaints only its own rect.

spinner' :: forall (es :: [Effect]). Ui :> es => Eff es Response Source #

spinnerWith :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Float -> Eff es () Source #

spinner with a layout modifier and a diameter in pixels.

spinnerWith' :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Float -> Eff es Response Source #

data Inline Source #

A piece of a paragraph: text in one style, and the hyperlink it follows when it is one. A string literal is plain text.

Instances

Instances details
IsString Inline Source # 
Instance details

Defined in NanoUI.Widgets.RichText

Methods

fromString :: String -> Inline #

inlineText :: Text -> Inline Source #

Text in the paragraph's own style.

inlineWith :: (Layout -> Layout) -> Text -> Inline Source #

Text styled by font modifiers (fontBold, fontSize 20, fontColor red . fontUnderline), applied over the paragraph's layout.

restyle :: (Layout -> Layout) -> Inline -> Inline Source #

Add font modifiers to a piece, a hyperlink included.

strong :: Text -> Inline Source #

Bold text.

emphasis :: Text -> Inline Source #

Italic text.

inlineCode :: Text -> Inline Source #

Monospaced text.

hyperlink :: Text -> Text -> Inline Source #

hyperlink target label: text in the theme's link colour, underlined while hovered, whose click the paragraph reports as target.

richText :: forall (es :: [Effect]). Ui :> es => [Inline] -> Eff es (Maybe Text) Source #

A paragraph of pieces, wrapped at its width. Returns the target of the hyperlink clicked this frame.

richText' :: forall (es :: [Effect]). Ui :> es => [Inline] -> Eff es (Response, Maybe Text) Source #

richTextWith :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> [Inline] -> Eff es (Maybe Text) Source #

richText with a layout modifier, whose font choices are the default for every piece.

richTextWith' :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> [Inline] -> Eff es (Response, Maybe Text) Source #

sparkline :: forall (es :: [Effect]). Ui :> es => [Float] -> Eff es () Source #

A small line chart of the values, 80 by 24 px, scaled to their range.

sparkline' :: forall (es :: [Effect]). Ui :> es => [Float] -> Eff es Response Source #

sparklineWith :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Float -> Float -> [Float] -> Eff es () Source #

sparkline with a layout modifier and a width and height in pixels.

sparklineWith' :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Float -> Float -> [Float] -> Eff es Response Source #

Images and drawing

newtype ImageId Source #

Constructors

ImageId 

Fields

Instances

Instances details
Eq ImageId Source # 
Instance details

Defined in NanoUI.Types

Methods

(==) :: ImageId -> ImageId -> Bool #

(/=) :: ImageId -> ImageId -> Bool #

Ord ImageId Source # 
Instance details

Defined in NanoUI.Types

Show ImageId Source # 
Instance details

Defined in NanoUI.Types

image :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> ImageId -> Eff es () Source #

An image registered with the host, sized by the layout modifier.

image' :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> ImageId -> Eff es Response Source #

image with its Response, for example to keepAnimating an image whose id changes over time.

freshImageId :: forall (es :: [Effect]). Ui :> es => Eff es ImageId Source #

An image id that no registered image uses and no earlier call returned. Take one for each image registered while the app runs.

registerImageRgba :: forall (es :: [Effect]). Ui :> es => ImageId -> Int -> Int -> ByteString -> Eff es Bool Source #

Register an RGBA image (4 bytes a pixel, rows top to bottom) under an id while the app runs, for image to draw. Returns False when the size or pixels are invalid, an image of another size already has the id, or the atlas is full. An image of the same size is replaced.

data Svg Source #

A parsed SVG document.

Instances

Instances details
Eq Svg Source #

Documents are equal when their sources hash the same.

Instance details

Defined in NanoUI.Svg

Methods

(==) :: Svg -> Svg -> Bool #

(/=) :: Svg -> Svg -> Bool #

Show Svg Source # 
Instance details

Defined in NanoUI.Svg

Methods

showsPrec :: Int -> Svg -> ShowS #

show :: Svg -> String #

showList :: [Svg] -> ShowS #

parseSvg :: Text -> Either String Svg Source #

Parse an SVG document.

loadSvg :: FilePath -> IO (Either String Svg) Source #

Read and parse an SVG file.

svgIcon :: forall (es :: [Effect]). Ui :> es => Float -> Svg -> Eff es () Source #

An SVG icon size logical pixels square, drawn in the text colour where it is used: a one-colour document (every paint currentColor or unspecified) takes the colour as a tint, and a multicoloured one paints its currentColor with it.

svgIconWith :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Svg -> Eff es () Source #

An SVG document sized by the layout modifier: a fixed width and height, or else the document's own size. A fontColor in the modifier replaces the text colour.

svgIconWith' :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Svg -> Eff es Response Source #

The document is rasterized once per pixel size and colour, at the display's scale, and kept in the image atlas for as long as the app runs.

svgSize :: Svg -> (Float, Float) Source #

The document's own width and height, from its width and height or else its viewBox.

box :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> Color -> Eff es () Source #

A solid rectangle sized by the layout modifier.

drawing :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> (Rect -> SmallArray DrawOp) -> Eff es Response Source #

Vector ops for a laid-out widget. Paint caches ops while width and height stay the same, then translates when the widget moves. Unversioned: the cache drops while the widget animates because the builder has no content key, and a builder that draws something else at the same size neither rebuilds nor repaints. Use drawingVersioned for output that changes, or customWidget without a key to have every frame rebuild and compare.

drawingVersioned :: forall (es :: [Effect]). Ui :> es => Int -> (Layout -> Layout) -> (Rect -> SmallArray DrawOp) -> Eff es Response Source #

Like drawing, but the tessellated op cache is keyed by an explicit content version. Change the version whenever the builder output changes (a model pointer, dirty counter, or content hash): that rebuilds the ops and repaints the widget. Frames with the same version replay cached ops without rebuilding, even while the widget animates. Version 0 means unversioned, as in drawing.

drawingCached :: forall (es :: [Effect]). Ui :> es => Double -> Double -> Float -> Int -> (Layout -> Layout) -> IO Layout -> DrawingBuild -> Eff es Response Source #

Like drawingVersioned, but the layout itself comes from compute, which only reruns when the envelope, line height, content key, or modifier result change.

data DrawOp Source #

Constructors

FillRect !Rect !Color 
FillRoundedRect !Rect !Float !Color 
FillTriangle !Float !Float !Float !Float !Float !Float !Color 
FillCircle !Float !Float !Float !Color 
Stroke !Float !Float !Float !Float !Float !Color 
StrokeRoundedRect !Rect !Float !Float !Color 
StrokeCircle !Float !Float !Float !Float !Color 
StrokeLineAA !Float !Float !Float !Float !Float !Color 
FillQuadGradient !Rect !Color !Color !Color !Color 
DrawImageRect !Rect !Int !Float !Float !Float !Float !Color 
DrawText !Float !Float !Float !Float !Text !Color

Pen at (x, y) is the alignment point. ax 0..1 is left..right. ay 0..1 is bottom..top. ay < 0 means baseline (x is left, y is the baseline). Glyph size is the host font (drawTextBox).

DrawTextStyled !Float !Float !TextFont !Text !Color

Text in a font of its own, its line box's top left corner at (x, y).

Instances

Instances details
Eq DrawOp Source # 
Instance details

Defined in NanoUI.Draw.Types

Methods

(==) :: DrawOp -> DrawOp -> Bool #

(/=) :: DrawOp -> DrawOp -> Bool #

data TextFont Source #

The font a DrawTextStyled draws with: the same choices a label's layout makes.

Instances

Instances details
Eq TextFont Source # 
Instance details

Defined in NanoUI.Draw.Types

Show TextFont Source # 
Instance details

Defined in NanoUI.Draw.Types

defaultTextFont :: TextFont Source #

The theme's regular font.

drawTextBox :: FontMetrics -> Float -> Float -> Float -> Float -> Text -> Rect Source #

Pixel box for a DrawText using host advances. diagrams text has no envelope, so plot sizing uses this instead of fontSizeL.

shiftDrawOp :: Float -> Float -> DrawOp -> DrawOp Source #

Translate every vertex in a DrawOp. Paint reuses ops when only (x, y) moved.

Custom widgets

data CustomWidgetSpec a Source #

Complete specification for defining a custom widget.

Constructors

CustomWidgetSpec 

Fields

  • widgetLayout :: !Layout

    Flex layout constraints (widthheight sizing, minmax, alignment, padding).

  • widgetMeasure :: !(Maybe CustomMeasureFn)

    Optional intrinsic measurement hook for Fit or dynamic sizing.

  • widgetDraw :: !CustomDrawBuild

    Vector drawing procedure receiving interaction context and layout rect.

  • widgetContent :: !Int

    Content key: a number that changes whenever widgetDraw would draw something different from the state it reads (a value, a flag, a model revision; contentKey hashes numbers into one). A frame whose key, size, interaction state and metrics are unchanged neither rebuilds the ops nor repaints the widget (a widget that only moved has its ops translated), so key a drawing whose ops are expensive to build. The default 0 means no key: the ops are rebuilt every frame and compared, which repaints correctly whatever the drawing reads but pays for the rebuild. A stale key draws stale pixels, so derive it from everything the drawing reads, an animated value included: a key is believed while the widget animates, as a versioned drawing's version is.

  • widgetCursor :: !(Maybe (CustomDrawContext -> UiCursorKind))

    Optional custom mouse cursor when pointer is over the widget.

  • widgetFocusable :: !Bool

    Whether this widget accepts tab/keyboard focus.

  • widgetDamageSlop :: !Float

    Padding added to dirty rectangles (for shadows, glow, or drag handles).

  • widgetInteract :: !(Response -> CustomDrawContext -> Input -> (Response, a))

    Interaction hook. It receives the widget's resolved Response (hover, press, right-click, and clicks including one queued from a previous frame), the draw context and the input, and returns the final response and value.

defaultCustomWidgetSpec :: CustomWidgetSpec () Source #

Default configuration for a custom widget with standard hoverpressclick behavior.

customWidget :: forall (es :: [Effect]) a. Ui :> es => CustomWidgetSpec a -> Eff es (Response, a) Source #

Instantiates a custom widget from a CustomWidgetSpec.

Connects the widget into: - The two-pass layout arena (respecting widgetMeasure or layout constraints). - Off-heap vector drawing pipeline. Without a widgetContent key the draw function runs once a frame and the widget repaints when its ops change, so it may read anything; with one, an unchanged key skips both. - Interactive hit-testing, focus management, and custom cursor resolution. - Accurate damage region tracking with widgetDamageSlop.

customWidgetWithId :: forall (es :: [Effect]) a. Ui :> es => WidgetId -> CustomWidgetSpec a -> Eff es (Response, a) Source #

Instantiates a custom widget using an existing WidgetId.

contentKey :: [Float] -> Int Source #

A widgetContent key for a drawing whose output follows these numbers. Pass every value the drawing reads; 0 means "no key", so a hash that lands there becomes 1.

type CustomMeasureFn = FontMetrics -> (Float, Float) -> (Float, Float) Source #

Custom node measurement: font metrics and available (width, height) to the node's desired (width, height).

data CanvasM a Source #

Monadic canvas builder that collects DrawOp vector operations efficiently.

Instances

Instances details
Applicative CanvasM Source # 
Instance details

Defined in NanoUI.Widgets.Custom

Methods

pure :: a -> CanvasM a #

(<*>) :: CanvasM (a -> b) -> CanvasM a -> CanvasM b #

liftA2 :: (a -> b -> c) -> CanvasM a -> CanvasM b -> CanvasM c #

(*>) :: CanvasM a -> CanvasM b -> CanvasM b #

(<*) :: CanvasM a -> CanvasM b -> CanvasM a #

Functor CanvasM Source # 
Instance details

Defined in NanoUI.Widgets.Custom

Methods

fmap :: (a -> b) -> CanvasM a -> CanvasM b #

(<$) :: a -> CanvasM b -> CanvasM a #

Monad CanvasM Source # 
Instance details

Defined in NanoUI.Widgets.Custom

Methods

(>>=) :: CanvasM a -> (a -> CanvasM b) -> CanvasM b #

(>>) :: CanvasM a -> CanvasM b -> CanvasM b #

return :: a -> CanvasM a #

runCanvas :: CanvasM a -> SmallArray DrawOp Source #

Compile a CanvasM block into an immutable 'SmallArray DrawOp'.

canvas :: forall (es :: [Effect]). Ui :> es => (Layout -> Layout) -> (Rect -> CanvasM ()) -> Eff es Response Source #

Draw into a rectangle sized by the layout modifier. Use customWidget when the drawing needs hover or press state.

drawRect :: Rect -> Color -> CanvasM () Source #

Fill a solid rectangle.

drawRoundedRect :: Rect -> Float -> Color -> CanvasM () Source #

Fill a rounded rectangle with given corner radius.

drawCircle :: V2 -> Float -> Color -> CanvasM () Source #

Fill a solid circle at center with given radius.

drawStroke :: V2 -> V2 -> Float -> Color -> CanvasM () Source #

Stroke a straight segment between two points with thickness.

drawStrokeRoundedRect :: Rect -> Float -> Float -> Color -> CanvasM () Source #

Stroke a rounded rectangle border with given radius and stroke width.

drawStrokeCircle :: V2 -> Float -> Float -> Color -> CanvasM () Source #

Stroke a circular outline at center with given radius and stroke width.

drawStrokeAA :: V2 -> V2 -> Float -> Color -> CanvasM () Source #

Antialiased smooth stroke line between two points.

drawQuadGradient :: Rect -> Color -> Color -> Color -> Color -> CanvasM () Source #

Four-corner bilinear gradient fill (top-left, top-right, bottom-right, bottom-left).

drawLinearGradientH :: Rect -> Color -> Color -> CanvasM () Source #

Horizontal 2-color linear gradient fill (left to right).

drawLinearGradientV :: Rect -> Color -> Color -> CanvasM () Source #

Vertical 2-color linear gradient fill (top to bottom).

drawImage :: Rect -> ImageId -> Color -> CanvasM () Source #

Draw a textured image stretched over given rectangle.

drawImageUV :: Rect -> ImageId -> Float -> Float -> Float -> Float -> Color -> CanvasM () Source #

Draw a sub-region of a textured image with explicit UV texture coordinates.

drawText :: V2 -> AlignX -> AlignY -> Text -> Color -> CanvasM () Source #

Draw text positioned at a reference point with horizontal and vertical alignment.

useDrag2D :: forall (es :: [Effect]). Ui :> es => Rect -> Eff es Drag2D Source #

Tracks pointer dragging across a 2D area (e.g. for color pickers, joysticks, canvas panning).

data Drag2D Source #

Result of a 2D drag gesture.

Constructors

Drag2D 

Fields

  • dragPosition :: !V2

    Current dragged pointer position clamped within bounds.

  • dragActive :: !Bool

    True while pointer is pressed and dragging is active.

  • dragDelta :: !V2

    Movement delta since previous frame.

Instances

Instances details
Eq Drag2D Source # 
Instance details

Defined in NanoUI.Widgets.Custom

Methods

(==) :: Drag2D -> Drag2D -> Bool #

(/=) :: Drag2D -> Drag2D -> Bool #

Show Drag2D Source # 
Instance details

Defined in NanoUI.Widgets.Custom

useWheelDelta :: forall (es :: [Effect]). Ui :> es => Rect -> Eff es (Float, Float) Source #

Inspects mouse wheel scroll delta when pointer is hovering over bounds.

Drag and drop

data DropType Source #

OS-level drag-and-drop event kind, mirroring SDL_EventType drop codes.

Constructors

DropBegin

A drag enters the window; no position or payload yet.

DropPosition

The drag pointer moved over the window; position available.

DropFile

A file path was dropped; dropEventData holds the path.

DropText

Text was dropped; dropEventData holds the text.

DropComplete

The OS drag operation finished.

Instances

Instances details
Eq DropType Source # 
Instance details

Defined in NanoUI.Input

Show DropType Source # 
Instance details

Defined in NanoUI.Input

data DropEvent Source #

A single normalized drop payload surfaced to widgets.

Constructors

DropEvent 

Instances

Instances details
Eq DropEvent Source # 
Instance details

Defined in NanoUI.Input

Show DropEvent Source # 
Instance details

Defined in NanoUI.Input

data DropTarget Source #

Per-frame drop state for a single rectangular drop target.

Constructors

DropTarget 

Fields

Instances

Instances details
Eq DropTarget Source # 
Instance details

Defined in NanoUI.Widgets.Drop

Show DropTarget Source # 
Instance details

Defined in NanoUI.Widgets.Drop

useDrop :: forall (es :: [Effect]). Ui :> es => Rect -> Eff es DropTarget Source #

Compute the drop state for a rectangle from the current frame's drop events.

Active/hover state persists across frames in the widget store, so a target keeps highlighting while the OS drag is stationary. Payload events (DropFile/DropText) are one-shot: they are reported exactly on the frame they arrive.

Attribution uses the tracked drag position (the coordinates of the most recent DropPosition) rather than a payload's own coordinates. SDL synthesizes file/text events at the last drag position and reports (0,0) when it never observed one, so the position stream is the only reliable signal for "which target is this drop over".

dropZone :: forall (es :: [Effect]) a. Ui :> es => (Layout -> Layout) -> Eff es a -> Eff es (a, Response, DropTarget) Source #

A panel that is also a drop target. Returns the body's result, the panel's Response, and the DropTarget for its rect.

Local state

useState :: forall a (es :: [Effect]). (Typeable a, Eq a, Ui :> es) => a -> Eff es (a, a -> Eff es ()) Source #

useFlag :: forall (es :: [Effect]). Ui :> es => Bool -> Eff es (Bool, Bool -> Eff es ()) Source #

useToggle :: forall (es :: [Effect]). Ui :> es => Bool -> Eff es (Bool, Eff es ()) Source #

useInt :: forall (es :: [Effect]). Ui :> es => Int -> Eff es (Int, Int -> Eff es ()) Source #

useFloat :: forall (es :: [Effect]). Ui :> es => Float -> Eff es (Float, Float -> Eff es ()) Source #

useEnum :: forall a (es :: [Effect]). (Enum a, Ui :> es) => a -> Eff es (a, a -> Eff es ()) Source #

useText :: forall (es :: [Effect]). Ui :> es => Text -> Eff es (Text, Text -> Eff es ()) Source #

Scrolling

A scroll container (scroll, scroll2D) handles the wheel and its own scrollbars. These move one from the outside, keyed by the WidgetId that scrollArea and scrollArea2D hand back.

How far the wheel goes, and whether a scroll glides onto its target instead of jumping, is one setting for the whole app:

setScrollTuning ctx defaultScrollTuning
  { scrollWheelStep = 3 * rowHeight  -- three rows a notch
  , scrollSmoothTime = 0.12          -- glide onto it
  }

setScrollStep gives one list a step of its own. With a glide time set, every wheel notch and every ScrollSmooth command eases onto its target over that many seconds, and the frame loop keeps drawing until it lands.

scrollIntoView brings a widget inside the scroller into view: the row a keyboard selection just moved to, say. A list that only builds the rows it shows has no widget to point at for the rest, so scroll to where the row would be with scrollRectIntoView, whose rectangle is in content coordinates. getScrollMetrics reports the viewport, range and offset such a list needs to pick its visible rows in the first place.

data ScrollTuning Source #

How far one wheel notch scrolls, and how long a scroll takes to settle. One setting for the whole context; a single scroller can take its own step (see setScrollStep).

Constructors

ScrollTuning 

Fields

  • scrollWheelStep :: Float

    Pixels one wheel notch scrolls. The default is three text lines, which is what Windows and most desktops send a notch as.

  • scrollSmoothTime :: Float

    Seconds a scroll takes to cover most of the distance to its target. 0 (the default) lands on it in the same frame.

Instances

Instances details
Eq ScrollTuning Source # 
Instance details

Defined in NanoUI.Context.Types

Show ScrollTuning Source # 
Instance details

Defined in NanoUI.Context.Types

getScrollTuning :: Context -> IO ScrollTuning Source #

Wheel step and glide time for every scroller in this context.

setScrollTuning :: Context -> ScrollTuning -> IO () Source #

Set the wheel step and glide time. Raising scrollWheelStep makes the wheel cover more ground per notch; a nonzero scrollSmoothTime turns every wheel notch and every ScrollSmooth command into a glide.

getScrollStep :: Context -> WidgetId -> IO Float Source #

This scroller's own wheel step, or 0 when it follows the context's.

setScrollStep :: Context -> WidgetId -> Float -> IO () Source #

Give one scroller its own wheel step, in pixels per notch. 0 puts it back on the context's step. A list whose rows are a fixed height reads best at a whole number of rows per notch.

data ScrollMetrics Source #

What a scroller looked like on the frame it was last laid out on. Offsets and ranges are in window axes: x rightwards, y downwards, whichever way the scroller itself is built.

Constructors

ScrollMetrics 

Fields

Instances

Instances details
Eq ScrollMetrics Source # 
Instance details

Defined in NanoUI.Context.Scroll

Show ScrollMetrics Source # 
Instance details

Defined in NanoUI.Context.Scroll

data ScrollAxes Source #

Which axes a scroller moves on, and how an offset in window axes (x rightwards, y downwards) maps onto its stored offset. A 1D row scroller keeps its offset in the main-axis slot, so its horizontal offset is the one that needs swapping.

Instances

Instances details
Eq ScrollAxes Source # 
Instance details

Defined in NanoUI.Context.Types

Show ScrollAxes Source # 
Instance details

Defined in NanoUI.Context.Types

getScrollMetrics :: Context -> WidgetId -> IO (Maybe ScrollMetrics) Source #

Geometry of the scroller wid, or Nothing before it has been laid out. Reads the last frame's layout, so it is safe to call while building the next one.

data ScrollBehavior Source #

Whether a scroll lands on its target at once or glides onto it. ScrollSmooth still lands at once when the context's scrollSmoothTime is 0, so one setting turns smooth scrolling on for the whole app.

Instances

Instances details
Eq ScrollBehavior Source # 
Instance details

Defined in NanoUI.Context.Scroll

Show ScrollBehavior Source # 
Instance details

Defined in NanoUI.Context.Scroll

data ScrollAlign Source #

Where a widget ends up in the viewport once it is scrolled into view.

Constructors

ScrollNearest

Move as little as possible: nothing at all when it is already whole.

ScrollStart

Against the leading edge, at the top or left.

ScrollCenter 
ScrollEnd

Against the trailing edge, at the bottom or right.

Instances

Instances details
Eq ScrollAlign Source # 
Instance details

Defined in NanoUI.Context.Scroll

Show ScrollAlign Source # 
Instance details

Defined in NanoUI.Context.Scroll

scrollTo :: Context -> WidgetId -> V2 -> ScrollBehavior -> IO () Source #

Scroll to an absolute offset, clamped to the scroller's range.

scrollBy :: Context -> WidgetId -> V2 -> ScrollBehavior -> IO () Source #

Scroll by a delta in pixels. Deltas accumulate onto a glide already in flight, so repeated calls keep up rather than fighting each other.

scrollPages :: Context -> WidgetId -> V2 -> ScrollBehavior -> IO () Source #

Scroll by whole viewports: V2 0 1 is one page down, V2 0 (-0.5) half a page up.

scrollToStart :: Context -> WidgetId -> ScrollBehavior -> IO () Source #

Scroll back to the top (and left).

scrollToEnd :: Context -> WidgetId -> ScrollBehavior -> IO () Source #

Scroll to the end of the content.

scrollIntoView :: Context -> WidgetId -> WidgetId -> ScrollAlign -> ScrollBehavior -> IO () Source #

Scroll target into the viewport of the scroller wid it is built inside. Both widgets are read from the last frame's layout, so a widget that was not built then, such as a row a virtualized list left out, cannot be found; scroll to its content rectangle with scrollRectIntoView instead.

scrollRectIntoView :: Context -> WidgetId -> Rect -> ScrollAlign -> ScrollBehavior -> IO () Source #

Scroll a rectangle of the content into view. The rectangle is in content coordinates: the origin is where the content starts, which is where the viewport shows it at offset 0.

setScrollOffset :: Context -> WidgetId -> Float -> IO () Source #

Move a scroller to an offset along its main axis. Cancels a glide in flight: whoever sets an offset outright owns it.

setScrollOffset2D :: Context -> WidgetId -> V2 -> IO () Source #

Move a scroller to an offset on both axes. Cancels a glide in flight.

Animation

data Transition Source #

How an animated value moves.

Constructors

Tween !Ease !Float !Float

Eased tween: duration and start delay, in seconds.

Spring !SpringParams

Damped spring; retargets from its current position and velocity.

animate :: forall (es :: [Effect]). Ui :> es => Transition -> Float -> Float -> Eff es Float Source #

Animate from from to to. It starts over from from once it has finished (a tween completes, a spring settles) or its tween changes, so calling it every frame cycles.

animateTo :: forall (es :: [Effect]). Ui :> es => Transition -> Float -> Eff es Float Source #

Animate from the current value toward target. An unchanged target keeps the running animation; a new one retargets from wherever the value is.

animateToA :: forall a (es :: [Effect]). (Animatable a, Ui :> es) => Transition -> a -> Eff es a Source #

animateTo for every component of a composite value.

pulse :: forall (es :: [Effect]). Ui :> es => Float -> Eff es Float Source #

A smoothly oscillating value in [0,1] driven by the real-time clock, with the given period in seconds (e.g. pulse 6 sweeps once every six seconds). The time is captured in Double (see uiTime), so the sweep stays sub-frame smooth even on long-running processes. The value is re-evaluated each frame, like animate.

keepAnimating :: forall r (es :: [Effect]). (HasResponse r, Ui :> es) => r -> Eff es () Source #

Keep a widget animating indefinitely so the frame loop never idles. Widgets driven by the wall clock (pulse, or drawing from uiTime) rather than by a frame-counted animation would otherwise stop repainting once other animations settle.

bar <- progressBar' =<< pulse 6
keepAnimating bar

class Animatable a where Source #

Instances

Instances details
Animatable Color Source # 
Instance details

Defined in NanoUI.Animatable

Animatable V2 Source # 
Instance details

Defined in NanoUI.Animatable

Animatable Double Source # 
Instance details

Defined in NanoUI.Animatable

Animatable Float Source # 
Instance details

Defined in NanoUI.Animatable

data Ease Source #

Instances

Instances details
Eq Ease Source # 
Instance details

Defined in NanoUI.Animation

Methods

(==) :: Ease -> Ease -> Bool #

(/=) :: Ease -> Ease -> Bool #

Show Ease Source # 
Instance details

Defined in NanoUI.Animation

Methods

showsPrec :: Int -> Ease -> ShowS #

show :: Ease -> String #

showList :: [Ease] -> ShowS #

data SpringParams Source #

Instances

Instances details
Eq SpringParams Source # 
Instance details

Defined in NanoUI.Animation

Show SpringParams Source # 
Instance details

Defined in NanoUI.Animation

Layout

data Sizing Source #

Instances

Instances details
Eq Sizing Source # 
Instance details

Defined in NanoUI.Style

Methods

(==) :: Sizing -> Sizing -> Bool #

(/=) :: Sizing -> Sizing -> Bool #

Show Sizing Source # 
Instance details

Defined in NanoUI.Style

data Direction Source #

Constructors

Row 
Column 

Instances

Instances details
Eq Direction Source # 
Instance details

Defined in NanoUI.Style

Bounded Direction Source # 
Instance details

Defined in NanoUI.Style

Enum Direction Source # 
Instance details

Defined in NanoUI.Style

Show Direction Source # 
Instance details

Defined in NanoUI.Style

data AlignX Source #

Instances

Instances details
Eq AlignX Source # 
Instance details

Defined in NanoUI.Style

Methods

(==) :: AlignX -> AlignX -> Bool #

(/=) :: AlignX -> AlignX -> Bool #

Bounded AlignX Source # 
Instance details

Defined in NanoUI.Style

Enum AlignX Source # 
Instance details

Defined in NanoUI.Style

Show AlignX Source # 
Instance details

Defined in NanoUI.Style

data AlignY Source #

AlignBaseline lines a row's text children up on their first baseline, and sits any other child on it by its bottom edge. Outside a row it is AlignTop.

Instances

Instances details
Eq AlignY Source # 
Instance details

Defined in NanoUI.Style

Methods

(==) :: AlignY -> AlignY -> Bool #

(/=) :: AlignY -> AlignY -> Bool #

Bounded AlignY Source # 
Instance details

Defined in NanoUI.Style

Enum AlignY Source # 
Instance details

Defined in NanoUI.Style

Show AlignY Source # 
Instance details

Defined in NanoUI.Style

data Padding Source #

Constructors

Padding 

Fields

Instances

Instances details
Eq Padding Source # 
Instance details

Defined in NanoUI.Style

Methods

(==) :: Padding -> Padding -> Bool #

(/=) :: Padding -> Padding -> Bool #

Show Padding Source # 
Instance details

Defined in NanoUI.Style

askDefaultLayout :: forall (es :: [Effect]). Ui :> es => Eff es Layout Source #

withDefaultLayout :: forall (es :: [Effect]) a. Ui :> es => (Layout -> Layout) -> Eff es a -> Eff es a Source #

alignBaseline :: Layout -> Layout Source #

Sit on the row's shared text baseline, so labels of different sizes read as one line of type. alignBottom lines up their boxes instead, and a larger font's deeper descent lifts its baseline above the smaller one's.

Text style

data TextDecoration Source #

Styling

A Theme says how every kind of widget looks: a Style for each surface (buttons, inputs, panels, floating windows) and colours for accents, text selection, links and so on. The context holds one theme for the whole app (setTheme); styled changes it for part of the view. Style and theme modifiers compose with (.) like layout modifiers do:

toolbar = styled (subtle . buttonStyle (cornerRadius 6)) $ row $ do
  whenM (button "Open") openFile
  styled primary (whenM (button "Save") save)

Scopes nest, and each one modifies the theme around it, so a modifier written once (primary, destructive, or one of your own) works in any theme. uiTheme reads the theme where it is called.

disabledWhen switches the widgets inside it off: they keep their layout and state, take no input, and fade toward the window colour.

styled :: forall (es :: [Effect]) a. Ui :> es => (Theme -> Theme) -> Eff es a -> Eff es a Source #

Draw a part of the view with a modified theme. Widgets declared inside take their colours, borders and corner radii from it, and styled scopes nest, each modifying the theme of the scope around it:

styled (buttonStyle (cornerRadius 8)) $ do
  styled primary (button "Save")
  button "Cancel"

The modifier runs once per scope per frame. The theme only affects how widgets look, never their layout.

themed :: forall (es :: [Effect]) a. Ui :> es => Theme -> Eff es a -> Eff es a Source #

Draw a part of the view with another theme, whatever the theme around it.

disabledWhen :: forall (es :: [Effect]) a. Ui :> es => Bool -> Eff es a -> Eff es a Source #

Disable every widget declared inside when the condition holds. Disabled widgets keep their place, state and layout, but take no pointer or keyboard input, cannot be focused, and are drawn with disabledTheme.

disabledWhen (T.null name) $ whenM (button "Save") save

uiTheme :: forall (es :: [Effect]). Ui :> es => Eff es Theme Source #

The theme the view is drawn with where this is called: the context theme as modified by the enclosing styled and disabledWhen scopes.

Style modifiers

fillColor :: Color -> Style -> Style Source #

A background with hover and press shades derived from it: hovering mixes in some of the foreground, pressing darkens.

Theme modifiers

inputStyle :: (Style -> Style) -> Theme -> Theme Source #

Text fields, text areas, sliders' wells and scroller wells.

panelStyle :: (Style -> Style) -> Theme -> Theme Source #

Panels, cards, menus, and label text.

windowStyle :: (Style -> Style) -> Theme -> Theme Source #

Floating windows.

textColor :: Color -> Theme -> Theme Source #

The foreground of every surface.

windowColor :: Color -> Theme -> Theme Source #

The backdrop behind everything, which disabled widgets also fade toward.

rounded :: Float -> Theme -> Theme Source #

The corner radius of every surface.

primary :: Theme -> Theme Source #

Buttons in the accent colour, for the action a view is for.

destructive :: Theme -> Theme Source #

Buttons in the theme's red, for destructive actions.

subtle :: Theme -> Theme Source #

Buttons without a fill or border until hovered, for toolbars and secondary actions.

tinted :: (Theme -> Color) -> Theme -> Theme Source #

Buttons filled with a colour picked from the theme, with a readable label.

styled (tinted themePurple) (button "Tag")

readableOn :: Theme -> Color -> Color Source #

Whichever of the theme's text colours reads best on c.

disabledTheme :: Theme -> Theme Source #

The theme disabled widgets are drawn with: every colour faded toward the window colour by themeDisabledFade, and no hover or press feedback.

Themes

data Theme Source #

Constructors

Theme 

Fields

Instances

Instances details
Eq Theme Source # 
Instance details

Defined in NanoUI.Style

Methods

(==) :: Theme -> Theme -> Bool #

(/=) :: Theme -> Theme -> Bool #

Show Theme Source # 
Instance details

Defined in NanoUI.Style

Methods

showsPrec :: Int -> Theme -> ShowS #

show :: Theme -> String #

showList :: [Theme] -> ShowS #

data Style Source #

Instances

Instances details
Eq Style Source # 
Instance details

Defined in NanoUI.Style

Methods

(==) :: Style -> Style -> Bool #

(/=) :: Style -> Style -> Bool #

Show Style Source # 
Instance details

Defined in NanoUI.Style

Methods

showsPrec :: Int -> Style -> ShowS #

show :: Style -> String #

showList :: [Style] -> ShowS #

defaultTheme :: Theme Source #

Neutral charcoal surfaces, warm text, and a blue selection accent. Keep structural edges quiet; interactive borders and focus carry contrast.

data Base16 Source #

Standard Base16 palette containing 16 styling tones and syntax colours following Chris Kempson's Base16 specification.

Constructors

Base16 

Fields

  • base00 :: !Color

    Default Background

  • base01 :: !Color

    Lighter Background (status bars, line numbers, panel backgrounds)

  • base02 :: !Color

    Selection Background (active elements, subtle highlights)

  • base03 :: !Color

    Comments, Invisibles, Line Highlighting (muted text, borders)

  • base04 :: !Color

    Dark Foreground (status bar foreground, secondary text)

  • base05 :: !Color

    Default Foreground, Caret, Delimiters, Operators

  • base06 :: !Color

    Light Foreground

  • base07 :: !Color

    Light Background / Highest contrast foreground

  • base08 :: !Color

    Variables, XML Tags, Markup Link Text, Markup Lists, Diff Deleted (Red)

  • base09 :: !Color

    Integers, Boolean, Constants, XML Attributes, Markup Link Url (Orange)

  • base0A :: !Color

    Classes, Markup Bold, Search Text Background (Yellow)

  • base0B :: !Color

    Strings, Inherited Class, Markup Code, Diff Inserted (Green)

  • base0C :: !Color

    Support, Regular Expressions, Escape Characters, Markup Quotes (Cyan)

  • base0D :: !Color

    Functions, Methods, Attribute IDs, Headings (Blue / Primary Accent)

  • base0E :: !Color

    Keywords, Storage, Selector, Markup Italic, Diff Changed (Purple / Magenta)

  • base0F :: !Color

    Deprecated, Opening/Closing Embedded Language Tags (Brown)

Instances

Instances details
Eq Base16 Source # 
Instance details

Defined in NanoUI.Style

Methods

(==) :: Base16 -> Base16 -> Bool #

(/=) :: Base16 -> Base16 -> Bool #

Show Base16 Source # 
Instance details

Defined in NanoUI.Style

themeFromBase16 :: Base16 -> Theme Source #

Calculate a Theme from a Base16 colorscheme, automatically selecting dark or light styling based on background vs foreground luminance.

themeFromBase16Dark :: Base16 -> Theme Source #

Calculate a dark Theme from a Base16 colorscheme.

themeFromBase16Light :: Base16 -> Theme Source #

Calculate a light Theme from a Base16 colorscheme.

base16TomorrowNight :: Base16 Source #

Tomorrow Night Base16 reference palette.

base16TomorrowLight :: Base16 Source #

Tomorrow Light Base16 reference palette.

withTheme :: Context -> Theme -> IO Context Source #

Configure a context's theme. Goes through setTheme so a theme swapped between frames invalidates the caches keyed on it, drawing-op caches included, instead of leaving widgets painting the previous theme.

setUiTheme :: forall (es :: [Effect]). Ui :> es => Theme -> Eff es () Source #

Geometry and colour

data V2 Source #

Constructors

V2 

Fields

Instances

Instances details
Eq V2 Source # 
Instance details

Defined in NanoUI.Types

Methods

(==) :: V2 -> V2 -> Bool #

(/=) :: V2 -> V2 -> Bool #

Show V2 Source # 
Instance details

Defined in NanoUI.Types

Methods

showsPrec :: Int -> V2 -> ShowS #

show :: V2 -> String #

showList :: [V2] -> ShowS #

Animatable V2 Source # 
Instance details

Defined in NanoUI.Animatable

data Rect Source #

Constructors

Rect 

Fields

Instances

Instances details
Eq Rect Source # 
Instance details

Defined in NanoUI.Types

Methods

(==) :: Rect -> Rect -> Bool #

(/=) :: Rect -> Rect -> Bool #

Show Rect Source # 
Instance details

Defined in NanoUI.Types

Methods

showsPrec :: Int -> Rect -> ShowS #

show :: Rect -> String #

showList :: [Rect] -> ShowS #

data Size Source #

Constructors

Size 

Fields

Instances

Instances details
Eq Size Source # 
Instance details

Defined in NanoUI.Types

Methods

(==) :: Size -> Size -> Bool #

(/=) :: Size -> Size -> Bool #

Show Size Source # 
Instance details

Defined in NanoUI.Types

Methods

showsPrec :: Int -> Size -> ShowS #

show :: Size -> String #

showList :: [Size] -> ShowS #

newtype Color Source #

Constructors

Color Word32 

Instances

Instances details
Eq Color Source # 
Instance details

Defined in NanoUI.Types

Methods

(==) :: Color -> Color -> Bool #

(/=) :: Color -> Color -> Bool #

Num Color Source # 
Instance details

Defined in NanoUI.Types

Show Color Source # 
Instance details

Defined in NanoUI.Types

Methods

showsPrec :: Int -> Color -> ShowS #

show :: Color -> String #

showList :: [Color] -> ShowS #

Animatable Color Source # 
Instance details

Defined in NanoUI.Animatable

contrastRatio :: Color -> Color -> Double Source #

WCAG 2 relative-luminance contrast. 4.5 is AA for normal text.

Alpha is ignored, so both colours must be opaque. Passing a translucent colour such as themeOverlayDim gives a meaningless ratio; composite it over its backdrop first.

v2Add :: V2 -> V2 -> V2 Source #

v2Sub :: V2 -> V2 -> V2 Source #

onGrid :: Float -> Float -> Float Source #

Round a logical coordinate onto the device-pixel grid implied by draw scale s (device px = logical * s). Every layer that positions pixels -- the layout solve, text pens, scroll offsets, paint and glyph rasterization -- must route its coordinates through this single function (backends through roundHalfUp), so geometry can never dephase from text. An identity when s <= 0 (no scaling).

roundHalfUp :: Float -> Int Source #

Round to the nearest integer, ties up: the device-pixel rounding shared by onGrid and the backends. Not ties-to-even (round): at a fractional scale (125%: a 20px row is 25 device px) a column of rows can all sit on half pixels, and ties-to-even would alternate them down and up, leaving uneven gaps. Compares the exact fractional part rather than floor (r + 0.5), whose addition itself rounds: it lifts the float just below 0.5 to 1 and odd integers past 2^23 up by one.

Input

data Key Source #

Instances

Instances details
Eq Key Source # 
Instance details

Defined in NanoUI.Input

Methods

(==) :: Key -> Key -> Bool #

(/=) :: Key -> Key -> Bool #

Bounded Key Source # 
Instance details

Defined in NanoUI.Input

Methods

minBound :: Key #

maxBound :: Key #

Enum Key Source # 
Instance details

Defined in NanoUI.Input

Methods

succ :: Key -> Key #

pred :: Key -> Key #

toEnum :: Int -> Key #

fromEnum :: Key -> Int #

enumFrom :: Key -> [Key] #

enumFromThen :: Key -> Key -> [Key] #

enumFromTo :: Key -> Key -> [Key] #

enumFromThenTo :: Key -> Key -> Key -> [Key] #

Show Key Source # 
Instance details

Defined in NanoUI.Input

Methods

showsPrec :: Int -> Key -> ShowS #

show :: Key -> String #

showList :: [Key] -> ShowS #

data Modifiers Source #

Constructors

Modifiers 

Fields

Instances

Instances details
Eq Modifiers Source # 
Instance details

Defined in NanoUI.Input

Show Modifiers Source # 
Instance details

Defined in NanoUI.Input

appendDropEvent :: DropEvent -> SmallArray DropEvent -> SmallArray DropEvent Source #

The drops with one more at the end.

foldInputKeys :: (a -> Key -> a) -> a -> SmallArray Key -> a Source #

Damage

data Damage Source #

Constructors

DamageFull 
DamageClip Rect 

Instances

Instances details
Eq Damage Source # 
Instance details

Defined in NanoUI.Types

Methods

(==) :: Damage -> Damage -> Bool #

(/=) :: Damage -> Damage -> Bool #

Show Damage Source # 
Instance details

Defined in NanoUI.Types

data DamageBounds Source #

Invalidation bounding strategy for a widget and its interaction events.

Constructors

DamageSelf

Exact layout bounding box Rect

DamageInflated !Float

Layout bounding box inflated by margin (focus rings, shadows, text slop)

DamageExact !Rect

Explicit rectangle in window space

DamageCustom (Rect -> Rect)

Custom transformation on layout bounding box

DamageUnion !DamageBounds !DamageBounds

Combined invalidation bounds

DamageNone

No invalidation bounds

Instances

Instances details
Eq DamageBounds Source # 
Instance details

Defined in NanoUI.Types

Show DamageBounds Source # 
Instance details

Defined in NanoUI.Types

defaultDamageSlop :: Float Source #

Standard damage slop for text overhang, focus rings, and border anti-aliasing.

sliderDamageSlop :: Float Source #

Damage slop for slider handles that extend past track bounds.

haloDamageSlop :: Float Source #

Damage slop for window resize halos and shadows.

resolveDamageRect :: DamageBounds -> Rect -> Rect Source #

Resolve damage bounds against a given layout rect.

damageWidgetNow :: forall (es :: [Effect]). Ui :> es => WidgetId -> DamageBounds -> Eff es () Source #

damageKeyNow :: forall (es :: [Effect]). Ui :> es => Int -> DamageBounds -> Eff es () Source #

damageRectNow :: forall (es :: [Effect]). Ui :> es => Rect -> Eff es () Source #

damageGroupNow :: forall (es :: [Effect]). Ui :> es => [WidgetId] -> DamageBounds -> Eff es () Source #

damageFullNow :: forall (es :: [Effect]). Ui :> es => Eff es () Source #

Backend support

data FontMetrics Source #

Constructors

FontMetrics 

Fields

data FontBackend Source #

Text preparation performs font queries in IO and returns an immutable snapshot for pure layout. Rasterisation is separate and occurs during draw.

prepareFontMetricsMany :: FontMetrics -> [Text] -> IO FontMetrics Source #

Prepare a finite text workspace for pure multi-label layout algorithms.

drawShaped :: FontMetrics -> Text -> IO (Maybe ShapedGlyphs) Source #

The glyph quads of a shaped line, placing glyphs in the host's atlas as needed; Nothing when the host does not shape.

data GlyphQuad Source #

Constructors

GlyphQuad 

Fields

Instances

Instances details
Eq GlyphQuad Source # 
Instance details

Defined in NanoUI.Font

Show GlyphQuad Source # 
Instance details

Defined in NanoUI.Font

data ShapedText Source #

A line of text as the host's shaper laid it out: glyphs chosen and placed with the font's kerning, ligatures and contextual forms, in fallback fonts where the font lacks a character, and right-to-left runs reordered.

Constructors

ShapedText 

Fields

  • stAdvance :: !Float
     
  • stInkEnd :: !Float

    The right edge of the rightmost glyph's ink.

  • stCarets :: !(PrimArray Float)

    Where the caret sits before each character, and after the last: one more entry than the text has characters. A right-to-left run's carets decrease, and the characters of a cluster share its width.

Instances

Instances details
Eq ShapedText Source # 
Instance details

Defined in NanoUI.Font

Show ShapedText Source # 
Instance details

Defined in NanoUI.Font

newtype ShapedGlyphs Source #

The glyph quads that draw a shaped line: eight numbers a glyph (x, y, width and height from the pen, then the atlas UVs u0 v0 u1 v1), in logical pixels. Valid until the host's glyph atlas next resets.

Constructors

ShapedGlyphs (PrimArray Float) 

Instances

Instances details
Eq ShapedGlyphs Source # 
Instance details

Defined in NanoUI.Font

Show ShapedGlyphs Source # 
Instance details

Defined in NanoUI.Font

uiFontMetrics :: forall (es :: [Effect]). Ui :> es => Eff es FontMetrics Source #

data ScrollBarSlot Source #

The layout arena stores a scroller's slot as its Enum value, and every other node reads a zero there, so ScrollBarList comes first.

scrollBarGutter :: ScrollBarSlot -> Float -> Float Source #

Space an overflowing scroller takes from its content, beside the padding trailPad on the bar's side, so the content stops one gap before the bar. A list bar keeps a gap to its well's edge as well. A page bar sits a side gap inside the page's edge. A window body's bar sits out in the window's padding, a side gap inside the window's edge, so that padding is the gap and only the bar and the side gap come out of the content.

scrollBarWidth :: Float Source #

Thickness of a list or page scrollbar.

data Compact a #

A Compact contains fully evaluated, pure, immutable data.

Compact serves two purposes:

  • Data stored in a Compact has no garbage collection overhead. The garbage collector considers the whole Compact to be alive if there is a reference to any object within it.
  • A Compact can be serialized, stored, and deserialized again. The serialized data can only be deserialized by the exact binary that created it, but it can be stored indefinitely before deserialization.

Compacts are self-contained, so compacting data involves copying it; if you have data that lives in two Compacts, each will have a separate copy of the data.

The cost of compaction is fully evaluating the data + copying it. However, because compact does not stop-the-world, retaining internal sharing during the compaction process is very costly. The user can choose whether to compact or compactWithSharing.

When you have a Compact a, you can get a pointer to the actual object in the region using getCompact. The Compact type serves as handle on the region itself; you can use this handle to add data to a specific Compact with compactAdd or compactAddWithSharing (giving you a new handle which corresponds to the same compact region, but points to the newly added object in the region). At the moment, due to technical reasons, it's not possible to get the Compact a if you only have an a, so make sure you hold on to the handle as necessary.

Data in a compact doesn't ever move, so compacting data is also a way to pin arbitrary data structures in memory.

There are some limitations on what can be compacted:

  • Functions. Compaction only applies to data.
  • Pinned ByteArray# objects cannot be compacted. This is for a good reason: the memory is pinned so that it can be referenced by address (the address might be stored in a C data structure, for example), so we can't make a copy of it to store in the Compact.
  • Objects with mutable pointer fields (e.g. IORef, MutableArray) also cannot be compacted, because subsequent mutation would destroy the property that a compact is self-contained.

If compaction encounters any of the above, a CompactionFailed exception will be thrown by the compaction operation.

askCompact :: forall a (es :: [Effect]). (Typeable a, Ui :> es) => Eff es (Maybe a) Source #