{"id":27946689,"url":"https://github.com/elcritch/figuro","last_synced_at":"2025-05-07T13:57:06.806Z","repository":{"id":184944949,"uuid":"672726118","full_name":"elcritch/figuro","owner":"elcritch","description":"UI Library in pure Nim! [WIP]","archived":false,"fork":false,"pushed_at":"2025-05-07T06:18:01.000Z","size":3718,"stargazers_count":119,"open_issues_count":19,"forks_count":6,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-05-07T13:57:00.967Z","etag":null,"topics":["animations","css","css-grid","graphical-user-interface","gui","ui","ux"],"latest_commit_sha":null,"homepage":"","language":"Nim","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/elcritch.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2023-07-31T02:56:35.000Z","updated_at":"2025-05-07T13:20:43.000Z","dependencies_parsed_at":"2023-07-31T04:23:14.544Z","dependency_job_id":"f8786827-dec9-44ad-94a2-fc3a4f2dab6d","html_url":"https://github.com/elcritch/figuro","commit_stats":null,"previous_names":["elcritch/figura","elcritch/figuro"],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elcritch%2Ffiguro","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elcritch%2Ffiguro/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elcritch%2Ffiguro/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elcritch%2Ffiguro/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elcritch","download_url":"https://codeload.github.com/elcritch/figuro/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252892520,"owners_count":21820646,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["animations","css","css-grid","graphical-user-interface","gui","ui","ux"],"created_at":"2025-05-07T13:57:06.026Z","updated_at":"2025-05-07T13:57:06.791Z","avatar_url":"https://github.com/elcritch.png","language":"Nim","readme":"\n# Figuro\n\nFiguro is a open source framework for building *beautiful*, *interactive*, and *efficient* applications. It compiles small binaries with support for multiple platforms.\n\nBy building on some of the best parts of GUI development in last 15 years Figuro aims to incorporate the best elements of both imperitive and object oriented GUI toolkits.\n\nOriginally based on Fidget, Figuro has now diverged quite significantly and includes a multi-threaded core, typed widgets, reactive event system, and CSS theming.\n\n*Note*: Figuro is now in an *beta* stage with all the key pieces working. There's likely to be churn in the APIs but it's mostly stabalized. \n\n## Demo: HNBrowser\n\n![HNBrowser](examples/figuro-hnbrowser-demo.jpg)\n\nExample theming:\n\n![Theming](https://i.ibb.co/jPNGZkL7/HNBrowser.gif)\n\n## Example\n\n![Click Example](tests/scrolling-example.png)\n\nExample drawing buttons with a fading background when any of them are hovered (see below for how it works):\n\n```nim\nimport figuro/widgets/[button, scrollpane, vertical]\nimport figuro\n\nlet\n  font = UiFont(typefaceId: defaultTypeface, size: 22)\n\ntype\n  Main* = ref object of Figuro\n    value: float\n\nproc buttonItem(self, this: Figuro, idx: int) =\n  Button.new \"button\":\n    size 1'fr, 50'ux\n    fill rgba(66, 177, 44, 197).to(Color).spin(idx.toFloat*50)\n    if idx in [3, 7]:\n      size 0.9'fr, 120'ux\n\nproc draw*(self: Main) {.slot.} =\n  withWidget(self):\n    with this:\n      fill css\"#0000AA\"\n    ScrollPane.new \"scroll\":\n      offset 2'pp, 2'pp\n      cornerRadius 7.0'ux\n      size 96'pp, 90'pp\n      Vertical.new \"\":\n        offset 10'ux, 10'ux\n        itemHeight cx\"max-content\"\n        for idx in 0 .. 15:\n          buttonItem(self, this, idx)\n\nvar main = Main.new()\nvar frame = newAppFrame(main, size=(600'ui, 480'ui))\nstartFiguro(frame)\n\n```\n\n## Installation - Trying it out\n\nFiguro now uses Nimble by default.\n\nFirst install the latest Nimble version or at least version 0.16.4:\n```sh\nnimble install nimble@\\#head\n```\n\nNext install Figuro: \n```sh\nnimble install https://github.com/elcritch/figuro\n```\n\nAlternatively to run the tests you can do:\n```sh\ngit clone https://github.com/elcritch/figuro\ncd figuro \u0026\u0026 nimble install --deps\n```\n\n### Systems without OpenGL 3.3\n\nIt's possible to use EVGL:\n\n```sh\nnim c -d:useOpenGlEs ...\n```\n## Widgets\n\nFiguro builds Figuro (UI) nodes. Each node has a basic set of properties that can be set and a core set of events and methods somewhat similar to HTML DOM elements. The two core node types are Rectangle and Text. More node types can be created by making widgets.\n\nWidgets are nodes that sub-class a `Figuro` node type and provide a custom `draw` method (slot). The common way to create a Figuro app is creating a `Main` widget.\n\nHere's a minimal example of creating a blue rectangle:\n\n```nim\ntype Main* = ref object of Figuro\n\nproc draw*(self: Main) {.slot.} =\n  withWidget(self):\n    Rectangle.new \"body\":\n      # each widget template injects a new `this` variable\n      # that references the current widget\n\n      # sets the bounding box of this node\n      box this, 10'ux, 10'ux, 600'ux, 120'ux\n\n      # set the fill color\n      fill this, css\"00001F\"\n\nvar main = Main.new()\nlet frame = newAppFrame(main, size=(400'ui, 140'ui))\nframe.startFiguro()\n```\n\nNote that rectangle is used enough that it also has a shortcut template `rectangle` that can be used:\n\n```nim\nproc draw*(self: Main) {.slot.} =\n  withWidget(self):\n    rectangle \"body\":\n      # each widget template injects a new `this` variable\n```\n\n## Manual Node Setup\n\nThe follow example shows how to setup a child node without using the default `new` template. However, generally using the `new` templates is encouraged as the internals may need to change.\n\n```nim\nproc draw(self: Main) {.slot.} =\n  setName self, \"main\"\n  fill self, css\"#9F2B00\"\n  box self, 0'ux, 0'ux, 400'ux, 300'ux\n\n  let this = self\n  let childPreDraw = proc (c: Figuro) =\n    let this {.inject.} = Button[int](c)\n    box this, 40'ux, 30'ux, 80'ux, 80'ux\n    fill this, css\"#2B9F2B\"\n    let childPreDraw = proc (c: Figuro) =\n      let this {.inject.} = Text(c)\n      box this, 10'ux, 10'ux, 80'pp, 80'pp\n      fill this, blackColor\n      setText(this, [(font, \"testing\")], Center, Middle)\n    widgetRegisterImpl[Text](nkText, \"btnText\", this, childPreDraw)\n\n  # same as: widgetRegisterImpl[Button[int]](nkRectangle, \"btn\", this, childPreDraw)\n  let fc = FiguroContent(name: \"btn\", childInit: nodeInitRect[Button[int]], childPreDraw: childPreDraw)\n  this.contents.add(fc)\n```\n\n## Signals and Slots\n\nFiguro uses [Sigils](https://github.com/elcritch/sigils) which implements methods using signals and slots. Slots are methods which are connected to Signals on a given object and are invoked when the signal is trigged on that object.\n\nThis comes from [QT](https://doc.qt.io/qt-6/signalsandslots.html) and is a very powerful and flexible paradigm while still being very fast. It allows built-in reactive data types similar as well. It can also support \"deferred\" runs and support for running in threads or thread pools and largely prevents data-races.\n\nFinally it allows Figuro to be very flexible and to provide support for network RPCs, dynamic method realoding, and browser web-assembly in the future.\n\n## Goal\n\nMassive profits and world domination of course. ;) Failing that the ability to write cool UI apps easily, in pure Nim.\n\n### Useful Compilation Flags\n\n- `-d:debugLayout` prints a node tree with the layout of each node before and after computing a layout\n\n### Drawing model\n\nEach widget must inherit from the `Figuro` type. `Figuro` itself inherits from `Agent` which means it can work with signals \u0026 slots.\n\nEach widget is composed of a `draw` slot and a widget-macro which is exported using `exportWidget`. Draw slots expect the widget object to already be created.\n\nThe purpose of the widget macro which sets up a new block, calls `preNode()` and `postNode()` with the user code inserted into a anonymous callback. This callback is called by the `postNode()` proc by emitting a `doDraw` signal.\n\nEach `doDraw` signal on a widget is connected to multiple slots which ready a widget for drawing, runs pre-draw callbacks, run any widget draw slot, and runs post-draw callbacks. User code passed to the widget-macros become the pre-draw callback for that widget instance. For advanced needs, a post-draw callback can be manually supplied.  \n\n### Layout and Controlling Widget Size and Position\n\nThere are two modes of layout: basic and grid. Both of these use the same core set of layout constraints which can be used to configued the width \u0026 height or the offset in x \u0026 y. Normally layout constraints are referred to as just constraints for brevity. The basic APIs are `box`, `size`, and `offset` which all set layout constraints. Each widget has a `box` which can manually set the position, but can be overwritten by the layout system. It's recommended to avoid directly modifying it. Instead set `cxOffset` and `cxSize`.\n\nSimple example:\n\n```nim\nproc draw*(self: Main) {.slot.} =\n  nodes self:\n    fill \"#0000AA\"\n    size 100'pp, 100'pp ## this will set to 100 percent\n                        ## of the parents width and height\n                        ## Note this is a root object\n                        ## so it's parent is considered the window\n                        ## size\n    rectangle \"container\":\n      offset 20'ux, 20'ux ## offsets container 20'ux (aka 20'ui) points\n      size 90'pp, 80'pp ## sets width to 90 perc and 80 percent of parents width\n      clipContent true\n      cornerRadius 10.0\n      text \"val\":\n        ## No size or position given defaults to `UiNone`. This defaults\n        ## to the free size of it's parent after offsets are subtracted\n        setText({font: \"hello world!\"}, Center, Middle)\n\n```\n\nThe layout constraints are modeled on [CSS Grid](https://css-tricks.com/snippets/css/complete-guide-grid/) and for more advanced layouts understanding CSS Grid will be helpful. The reason for this is that CSS Grid is one of the most flexible layout systems avaialable on the web and yet remains simple to use once you understand the basics, unlike alternatives like flexbox or even raw table layouts.\n\nNote that the easiest way to set layout constraint values are to use their numeric literal types. These are:\n\n- `1'fr` for fraction\n- `1'ux` for fixed ui coordinates\n- `100'pp` for percentage\n- `cx\"auto\"` or `csAuto()` is the default and uses the full available size of it's parent size (current.wh = parent.wh - current.xy)\n- `1'ux` is equivalent to `1'ui` which is just a UICoord scalar\n- `ux(1+i*2)` to convert expressions to fixed ui coordinates\n- `cs\"min-content\"` minimum content size (currently grid layout only)\n- `cs\"max-content\"` minimum content size (currently grid layout only)\n\nHelper proc's for formula based constraints are `csFixed(x)`, `csMin(x,y)`, `csMax(x,y)`, `csMinMax(x,y)`, and `csMinMax(x,y)`. Note that the multi-argued constraints are still a WIP and don't work currently.\n\nInternally a layout constraint, normally shortened to just *constraint*, is formed from two pieces: the `Constraint` container object and an optional inner `ConstraintSize` object. \n\n#### CSS Grid Layout\n\nA CSS Grid layout allows you to create either a fixed pre-sized grid or a dynamically expandable grid.\n\n##### CSS Grid Automatic Vertical Layout\n\nThis example shows how to setup a *vertical group* using a CSS Grid with one full width column (set by `setGridCols 1'fr`). It grows by adding new rows with a height of `60ux` (set by `gridAutoRows 60ux`) whenver more child widgets are added. Items are vertically aligned (`alignItems CxStart`) and horizontally justified (`justifyItems CxCenter`). The child widgets have their sizes set to `size 60'ux, 40'ux`. Alternatively `CxStretch` could be used to force the child widgets to take up a whole column and row.\n\n```nim\n    rectangle \"main\":\n      fill whiteColor\n      offset 30'ux, 10'ux\n      size 400'ux, 120'ux\n\n      setGridCols 1'fr\n      setGridRows 60'ux\n      gridAutoRows 60'ux\n      gridAutoFlow grRow\n      justifyItems CxCenter\n      alignItems CxStart\n\n      rectangle(\"slider\"):\n        size 60'ux, 40'ux\n        fill \"#00A0AA\"\n      rectangle \"slider\":\n        size 60'ux, 40'ux\n        fill \"#A000AA\"\n```\n\n#### Constraint Reference Table\n\nHere's the full list of options (see CSS Grid for more details): \n\n```nim\ntype\n  ConstraintSizes* = enum\n    UiAuto ## default size option for nodes\n           ## it's the size of the parent width/height \n           ## minus the x/y positions of the node\n    UiFrac ## represents `fr` aka CSS Grid fractions\n    UiPerc ## represents percentage of parent box or grid\n    UiFixed ## represents fixed coordinate size\n    UiContentMin ## represents layout to use min-content, `cmin` is calculated internally\n    UiContentMax ## represents layout to use max-content, `cmax` is calculated internally\n\n  Constraints* = enum\n    UiValue ## holds a single `ConstraintSize`\n    UiMin ## minimum of lhs and rhs (partially supported)\n    UiMax ## maximum of lhs and rhs (partially supported)\n    UiSum ## sum of lhs and rhs (partially supported)\n    UiMinMax ## min-max of lhs and rhs (partially supported)\n    UiNone ## none item - excluded from CSS Grid layout \u0026 basic layout\n    UiEnd ## marks end track of a CSS Grid layout\n```\n\n### CSS Grid Layout Examples\n\nExample layouts using CSS Grid:\n- https://css-irl.info/solving-a-tricky-layout-problem/\n- https://apiumhub.com/tech-blog-barcelona/css-grid/\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felcritch%2Ffiguro","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felcritch%2Ffiguro","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felcritch%2Ffiguro/lists"}