nandi/cosmicnimpublic Fork 0
v0.7.0
Commits
Clone
git clone https://git.rickub.com/nandi/cosmicnim.git
git clone ssh://git@rickub.com/nandi/cosmicnim.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

cosmicnim.nim · 159 lines · 6.2 KBNim Blame HistoryRaw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
## Nim bindings for the cosmic_ffi cdylib.
##
## The window's shape is described from Nim, once per frame, inside an
## `onView` callback. Containers are block templates, so the widget tree in
## the source has the same shape as the widget tree on screen:
##
## ```nim
## b.container:
##   b.fill(); b.alignCenter(); b.spacing(space(SpaceM))
##   b.text("Count: 3", TextTitle1)
##   b.row:
##     b.spacing(space(SpaceS))
##     b.button("−", id = 0)
##     b.button("+", id = 1, style = ButtonSuggested)
## ```

import std/os

when not defined(noSignalHandler):
  {.warning: "compile with -d:noSignalHandler: libcosmic runs threads Nim " &
             "did not create, and Nim's signal handler allocates, so a signal " &
             "delivered to one of them crashes inside the handler".}

const libCosmicFfi* {.strdefine.} = currentSourcePath().parentDir /
                                    "libcosmic_ffi.so"
  ## Absolute at compile time, so the library is found wherever nimble put
  ## the package rather than only next to the binary. Override with
  ## `-d:libCosmicFfi=/some/other/libcosmic_ffi.so`.

type
  Builder* = distinct pointer
    ## Opaque. Only valid for the duration of the `onView` call it arrived in.

  OnView* = proc (ctx: pointer; b: Builder; width, height: cfloat) {.cdecl.}
    ## `width`/`height` are the pixels actually available — not what
    ## CosmicConfig asked for, which a tiling compositor will override.
  OnPress* = proc (ctx: pointer; id: int32) {.cdecl.}

  OnKey* = proc (ctx: pointer; key: cstring) {.cdecl.}
    ## Each key press: the typed character for ordinary keys ("7", "+"),
    ## otherwise the key's name ("Enter", "Backspace", "Escape",
    ## "ArrowLeft"). The string is only valid during the call.

  CosmicConfig* = object
    title*: cstring
    onView*: OnView
    onPress*: OnPress
    onKey*: OnKey      ## may be nil
    ctx*: pointer
    width*: uint32   ## 0 for a default
    height*: uint32  ## 0 for a default

  TextStyle* = enum
    TextBody, TextTitle1, TextTitle2, TextTitle3, TextTitle4,
    TextHeading, TextCaption, TextMonotext

  ButtonStyle* = enum
    ButtonStandard, ButtonSuggested, ButtonDestructive, ButtonText, ButtonLink

  SpaceStep* = enum
    SpaceNone, SpaceXxxs, SpaceXxs, SpaceXs, SpaceS, SpaceM, SpaceL, SpaceXl

const AbiVersion* = 6'u32
  ## The ABI these bindings are written against. Bumped whenever a signature
  ## changes, which is invisible to the dynamic linker: every symbol still
  ## resolves, and the arguments are simply read from the wrong registers.

proc cosmicAbiVersion*(): uint32
  {.cdecl, importc: "cosmic_abi_version", dynlib: libCosmicFfi.}

proc rawCosmicRun(config: ptr CosmicConfig): int32
  {.cdecl, importc: "cosmic_run", dynlib: libCosmicFfi.}

proc cosmicRun*(config: ptr CosmicConfig): int32 =
  ## Refuses to run against a library that speaks a different ABI, rather
  ## than letting the callbacks read whatever is in the registers.
  let found = cosmicAbiVersion()
  if found != AbiVersion:
    raise newException(LibraryError,
      "libcosmic_ffi speaks ABI " & $found & ", these bindings expect " &
      $AbiVersion & " -- run `just fetch` to get the matching release")
  rawCosmicRun(config)

# --- raw builder calls; prefer the wrappers below ---

proc beginColumn(b: Builder) {.cdecl, importc: "cosmic_column", dynlib: libCosmicFfi.}
proc beginRow(b: Builder) {.cdecl, importc: "cosmic_row", dynlib: libCosmicFfi.}
proc beginContainer(b: Builder) {.cdecl, importc: "cosmic_container", dynlib: libCosmicFfi.}
proc endNode(b: Builder) {.cdecl, importc: "cosmic_end", dynlib: libCosmicFfi.}

proc rawSpacing(b: Builder; px: cfloat) {.cdecl, importc: "cosmic_spacing", dynlib: libCosmicFfi.}
proc rawPadding(b: Builder; px: cfloat) {.cdecl, importc: "cosmic_padding", dynlib: libCosmicFfi.}
proc rawAlignCenter(b: Builder) {.cdecl, importc: "cosmic_align_center", dynlib: libCosmicFfi.}
proc rawFill(b: Builder) {.cdecl, importc: "cosmic_fill", dynlib: libCosmicFfi.}

proc rawText(b: Builder; style: int32; text: cstring)
  {.cdecl, importc: "cosmic_text", dynlib: libCosmicFfi.}
proc rawButton(b: Builder; style: int32; label: cstring; id: int32)
  {.cdecl, importc: "cosmic_button", dynlib: libCosmicFfi.}
proc rawSpace(b: Builder; w, h: cfloat)
  {.cdecl, importc: "cosmic_space", dynlib: libCosmicFfi.}
proc rawSize(b: Builder; w, h: cfloat)
  {.cdecl, importc: "cosmic_size", dynlib: libCosmicFfi.}
proc rawSpaceUnit(step: int32): cfloat
  {.cdecl, importc: "cosmic_space_unit", dynlib: libCosmicFfi.}

# --- containers ---

template column*(b: Builder; body: untyped) =
  ## Stack the widgets built in `body` vertically.
  beginColumn(b)
  body
  endNode(b)

template row*(b: Builder; body: untyped) =
  ## Lay the widgets built in `body` out horizontally.
  beginRow(b)
  body
  endNode(b)

template container*(b: Builder; body: untyped) =
  ## Wrap the widgets built in `body`, typically to pad or centre them.
  beginContainer(b)
  body
  endNode(b)

# --- attributes of the innermost open container ---

proc spacing*(b: Builder; px: float) = rawSpacing(b, cfloat(px))
proc padding*(b: Builder; px: float) = rawPadding(b, cfloat(px))
proc alignCenter*(b: Builder) = rawAlignCenter(b)
proc fill*(b: Builder) = rawFill(b)

# --- leaves ---

proc text*(b: Builder; s: string; style = TextBody) =
  rawText(b, int32(ord(style)), s.cstring)

proc button*(b: Builder; label: string; id: int32; style = ButtonStandard;
             enabled = true) =
  ## A button that reports `id` back through `onPress`. `enabled = false`
  ## draws it inert; `id` is then never delivered.
  rawButton(b, int32(ord(style)), label.cstring, if enabled: id else: -1)

proc space*(b: Builder; w, h: float) = rawSpace(b, cfloat(w), cfloat(h))

const Fill* = Inf
  ## As a dimension, take the space on offer. Siblings that all ask for it
  ## share the axis equally, which is how you get an even grid.

proc size*(b: Builder; w = -1.0; h = -1.0) =
  ## Size the next leaf, in pixels; `Fill` expands, a negative dimension
  ## stays natural. Applies to one leaf only, so set it before each widget
  ## you want sized.
  rawSize(b, cfloat(w), cfloat(h))

proc space*(step: SpaceStep): float =
  ## The active COSMIC theme's spacing for `step`, in pixels.
  float(rawSpaceUnit(int32(ord(step))))