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
|
## 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) {.cdecl.}
OnPress* = proc (ctx: pointer; id: int32) {.cdecl.}
CosmicConfig* = object
title*: cstring
onView*: OnView
onPress*: OnPress
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
proc cosmicRun*(config: ptr CosmicConfig): int32
{.cdecl, importc: "cosmic_run", dynlib: libCosmicFfi.}
# --- 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))
proc size*(b: Builder; w = -1.0; h = -1.0) =
## Size the next leaf, in pixels. 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))))
|