Skip to content

Releases: gitgusilva/gbui

gbui 0.3.2

Choose a tag to compare

@github-actions github-actions released this 16 Aug 22:37

Releases carry binaries now, and the site has a page to get them from.

Added

  • A shared library, the headers and the CMake package files, attached to
    every release
    — Linux x86-64, Windows x86-64, and both kinds of Mac. Unpack
    one, point CMAKE_PREFIX_PATH at it, and find_package(gbui) finds it;
    there is nothing else to install and nothing to build. A SHA256SUMS covers
    the set. Until now a release was a source tarball GitHub made for free, which
    is to say: build it yourself.

    They are built without the SDL2 backend, deliberately. A prebuilt library
    that links SDL2 needs the same SDL2 on the machine that loads it, and the
    version a distribution ships is never the version the runner had — so the one
    build that can be handed to a stranger is the one with nothing underneath it.
    Window::create returns nothing in these and every other part of the library
    is the same code. The alternative was a download that fails at load time on
    most machines, with a message about a shared object rather than about why.

    Three platforms, not four: macos-13 is retired, and a matrix entry naming a
    runner that no longer exists does not fail — it queues forever, and with
    needs: package the release never publishes. Intel Macs come back when there
    is a reason to add macos-15-intel.

    Each archive is consumed before it is allowed out: configured against,
    built against and run, on the machine that produced it, by
    tools/consumer. That is the check whose absence 0.3.1 was, and it now
    stands between a broken package and a published one rather than after it.

  • A download page, in the nav and in the guide's first section. It reads
    the newest release from the GitHub API at run time rather than baking a list
    into a page that gets deployed once and then goes quietly stale, puts the
    visitor's own platform first, and falls back to the releases page when the
    API rate-limits — sixty requests an hour per address, and a visitor left on a
    spinner has been told nothing.


Prebuilt libraries

A shared library, the headers and the CMake package files, for
each platform. Unpack one, point CMAKE_PREFIX_PATH at it and
find_package(gbui) finds it — the archive's README has the
three lines. Built without the SDL2 backend, because a
prebuilt library that links SDL2 needs the same SDL2 on your
machine; build from source if you want a window.

Verify with sha256sum -c SHA256SUMS.

gbui 0.3.1

Choose a tag to compare

@github-actions github-actions released this 16 Aug 21:25

A portability fix, and the two jobs that caught it. The library's own sources
are the same ones 0.3 shipped; what would not build is the test suite on a Mac
and the sample that links against an installed prefix.

Fixed

  • Modifiers had a reader for the platform's shortcut key and no writer.
    command() answers "Ctrl, or Cmd on a Mac", so everything that had to make
    such an event wrote the #ifdef again instead — two test harnesses so far,
    and the second spelt the field meta, which is not a field. Only one branch
    of a copy like that ever compiles, so the machine you are on cannot tell you:
    it built here and on both Linux jobs and failed on macOS. withCommand()
    holds down whichever key the reader reads, and both harnesses now call it.
  • components.hpp stopped exporting text and button and nothing noticed.
    Splitting "controls" into elements and components moved them to
    elements.hpp, which is what the guide has told callers to include ever
    since — but the consumer sample was still on the old umbrella. No in-tree
    build catches that, because everything in the tree includes the specific
    header it wants; the consumer is the only thing that asks the installed
    prefix for an umbrella and finds out what is in it. It now includes all four,
    which is the whole point of it.

gbui 0.3

Choose a tag to compare

@github-actions github-actions released this 16 Aug 21:11

The release that made the toolkit reachable. Everything below the components is
the same three-stage pipeline it was; what changed is that every control now
says what it is
, that saying so is a rule rather than a milestone, and that
seven components arrived — including the three the component inventory had named
as the gaps that bite first.

Added

  • splitPane — two panes and a divider the reader can drag, which is the
    shape every IDE-shaped application is built from.

    The share is a percentage basis rather than a grow ratio, and finding
    out why is the useful part: this layout engine computes its free space from
    the hypothetical sizes, which are already clamped to each item's minimum, so
    two panes with a 120-pixel floor take their 240 first and split only what is
    left — asking for a quarter of 600 got 208 instead of 148, and with large
    minimums the fraction stopped meaning anything. A basis of p% plus shrink
    is exact: the overflow the divider causes comes back off each pane in
    proportion to its basis, which lands the leading one at p × (width − divider).

    The minimums are the layout's rather than the drag's, so they hold when the
    window shrinks under a split nobody touched. The divider is ARIA's window
    splitter — a Separator that takes the keyboard and carries a value — because
    a split only draggable with a pointer is a layout most people cannot change.

  • treeView — the expandable hierarchy the component inventory calls the
    single biggest gap for a git client. Expansion, keyboard walking and
    virtualisation, which are each easy and never all three.

    The data is a flat vector in pre-order with a depth on each row. Flat is
    what makes virtualisation possible at all — a slice of a tree is only a slice
    if the tree is already a sequence — and it is what a caller usually has from a
    git ls-tree walk or a directory listing. Which rows are visible is worked
    out here in one pass with a watermark, so a caller that collapses a node
    passes exactly the same vector as before.

    Right opens a closed node and steps into an open one; Left closes an open one
    and steps out of a closed one. That pair is the whole of why a tree feels like
    a tree. The twisty opens without choosing and the row chooses, because "show
    me what is in here" and "I want this one" are two gestures.

    Each row reports its level and its position among its siblings — new
    Accessibility::level, ARIA's aria-level — since "item 2 of 5" in a
    hierarchy means whose five and "row 340 of 900" is the size of the repository
    rather than of the directory. Computed in two linear passes with a counter per
    depth, because the obvious version is quadratic on a directory with a thousand
    files in it, which is a directory people have.

    VirtualListOptions::itemRole is new with it: Role::None hands the slot's
    semantics to the row callback, so a tree's rows can be counted among their
    siblings rather than among the nine hundred the list holds.

  • select filters, which is the combobox the inventory called the gap that
    bites first
    — a branch picker past about thirty branches is unusable without
    type-to-filter. An option rather than a component of its own, for the reason
    textInput absorbed two fields: everything that makes a select a select is
    unchanged by typing into it, and the two would be one control described twice.

    SelectResult grew a focus, and it is the caller's half of the deal: a
    filter box has to hold the keyboard to be typed into, so the control cannot
    keep it on the closed box — and a component here never moves focus behind the
    caller's back. Same contract label and field already have. Not wiring it
    leaves a filter that works only once clicked.

    The highlight stays an index into the caller's list rather than into the
    filtered view of it, which is the invariant this is easiest to get wrong. The
    match is a case-insensitive substring rather than a fuzzy score, because fuzzy
    matching reorders the list under the reader and matches things they cannot see
    the reason for. Escape clears the filter before it closes the list; Space
    types a space instead of committing, since a combobox that cannot have a space
    in its query cannot find feat/nord tuning; the arrows walk what is on screen
    rather than stepping into rows that are not.

    The filter box carries controls and activeDescendant because that is where
    the keyboard is; the match count is a Status live region; and each row
    reports its place in what is shown, since "3 of 40" in a list narrowed to
    four is three lies in five words. MenuItemOptions grew positionInSet and
    setSize to carry it.

  • carousel — a strip of slides, one screenful at a time, with indicators,
    navigators, looping, a fractional slidesPerPage and autoplay. It moves by
    slides rather than by pages even when several are showing, which is the
    convention that keeps a four-across gallery usable: "next" is the thing after
    the one you are looking at.

    An autoplaying carousel always draws a pause button, and there is no option
    to remove it.
    WCAG's "pause, stop, hide" is a rule rather than a judgement,
    and an option to remove the button would be a switch labelled "make this
    inaccessible". Hovering the slides pauses it and so does the keyboard being
    inside them — but not reaching for a control, because the first attempt
    paused on focus anywhere in the carousel and pressing Play then left focus on
    Play and refused to move.

    Off-screen slides are hidden from the accessibility tree rather than left in
    it: eight slides all present at once turns a control into a list a reader has
    to find their way out of. The dots are a TabList with activeDescendant,
    one of the two patterns ARIA blesses for a carousel.

  • gallery — one picture at a time out of a set, with arrows, a caption and
    a thumbnail strip that keeps the current one in view. Every picture has a
    name: its alt, its caption, or "Image 3 of 9", because an unnamed picture in
    a set of nine is "image, image, image".

    Zoom, rotate, flip, download and fullscreen are absent, and each for a
    reason written into the header rather than left to be discovered: the first
    two need a transform on a node that the painter has not got, download needs a
    native file dialog and nothing here touches the filesystem, and fullscreen is
    a second window. A rotate button that does not rotate is worse than no button.

  • compare — two things in the same rectangle with a handle saying how much
    of each, which is the shape PrimeVue calls Compare and every before-and-after
    on the web is. Both sides are drawn at the full size of the box and one is
    revealed over the other, because a comparison laid out side by side is asking
    the reader to remember rather than to see.

    The seam is a percentage, not a measured width, and that is the whole
    design: a clip sized from last frame's geometry is a frame late and jumps on
    every resize, while a percentage resolves during layout and is right on the
    first frame. The content inside the clip is 100 / position percent of it,
    which comes back out to the full width — a layout identity rather than an
    arithmetic one. The handle is placed by two flexible spacers for the same
    reason, and that also keeps it wholly inside the box at either end.

    It is a slider and genuinely one — value, range, arrow keys at 2%, Page at
    10%, Home and End — rather than PrimeVue's hidden range input beside a div.
    The value is announced as "60% Retouched", because "60 percent" alone says
    neither how much of what nor revealing what, and both sides stay named in the
    tree whatever the handle is doing.

  • toast — short-lived messages, stacked in a corner and gone on their own.
    The last of the three the component inventory called blocking, and the one
    it described as "a queue, a timer and a live region".

    The queue is ToastState, owned by the application. That matters more here
    than usual: toasts are raised from anywhere — a network reply, a file watcher,
    a shortcut three screens away — and a component that owned them would be a
    component with a global.

    The id is the whole of the grouping. Two entries with the same id are one
    toast with a count on it, and an empty id is derived from the kind, the title
    and the message — so a retry loop reports "still offline ×40" instead of forty
    copies of one sentence, which is the failure every application's first toast
    queue has. group is a second and different axis: it routes an entry to an
    outlet, so a dialog can report into itself while the application's messages
    go to the corner.

    Placement is six corners, or anywhere at all. ToastPlacement::Anchored
    puts the stack against a tagged node with the same engine a popover uses, and
    bounds says which rectangle the corners are measured from, so a stack can
    live inside a panel. Which way it grows is never a decision the caller
    makes. A bottom stack does not measure itself to find its own bottom either —
    the container is the whole column and justify puts the toasts at the end of
    it, which is right on the first frame where arithmetic on last frame's height
    is not.

    The timer stops while it is being read, which is Toastify's behaviour and
    also what WCAG's "enough time" rule asks for: the stack pauses while the
    pointer is over a toast or the keyboard is inside it. duration = 0 never
    expires. Only what is on screen ages, so an entry waiting behind maxVisible
    has not started its clock. The progress bar is drawn only where there is a
    time to show, and dims while paused.

    Each toast is its own live region — Status for info and success, Alert for
    warning and error, because the next thing the reader was about to do will not
    work. The stack never takes the keyboard, an...

Read more

gbui 0.2.1

Choose a tag to compare

@gitgusilva gitgusilva released this 16 Aug 03:27

A packaging fix, released from 0.2.x rather than from main: the branch is
0.2 and this, and nothing else.

Fixed

  • A shared build could not run its own executables on Windows. Windows has
    no rpath — a program finds a DLL beside itself or on PATH — while CMake's
    multi-config generator puts the library in build/Release/ and every
    executable in build/<dir>/Release/. The build and the link both succeed and
    the loader fails at startup, which on a machine with no desktop is a dialog
    nobody can dismiss: the process never returns and never says why. The CI job
    that builds shared and runs the tests sat for six hours before it was
    cancelled. The DLLs are copied beside each executable now, on Windows and only
    when the build is shared.
  • CI grew the ceilings whose absence turned that bug into six hours:
    ctest --timeout 180, so a hung test fails with its own name, and
    timeout-minutes on the job that found it.

gbui 0.2

Choose a tag to compare

@gitgusilva gitgusilva released this 16 Aug 03:27

The first version with a published site, a CI pipeline, an installable library
and a set of application screens to look at. It is the version the documentation
described from the start; it had never been tagged, which is what this tag fixes
— an archived version with no tag is a dropdown entry that 404s.

The toolkit

  • Build → layout → paint, three stages in one direction: layout is
    arithmetic that can be asserted without a window, and painting is a display
    list that can be inspected without a GPU.
  • CSS flexbox with wrapping, percentages, min-content sizing and out-of-flow
    positioning, in logical pixels — a 200% display is a property of the output
    rather than something a component knows about.
  • Theming as data: 24 semantic tokens read from the gitbox-themes
    registry's JSON, with Material 3, Cupertino and Fluent palettes built in, and
    a Design beside them for shape, sizing and motion.
  • A software rasteriser with antialiasing, gradients and clipping, and an
    SVG writer for review and golden images.
  • The interaction layer in full: hover, press, click, focus,
    :focus-visible, focus-within, Tab traversal, wheel routing and per-node
    cursors.
  • An animation clock on CSS's transition model — a component says where a
    value should be, not how to get there.
  • Around fifty components, from a button to a table, a rich-text editor and
    eight kinds of chart, all stateless functions themed by token.
  • One allocation per frame: nodes live in an arena addressed by index, so
    building a tree is a push_back and releasing one is a reset.

The component set as data

  • gbui::meta describes every component — its group, its documentation, its
    signature, and each option with type, default and doc — generated from the
    headers
    by tools/generate_meta.py, so a table nobody maintains cannot fall
    behind the code it describes. CI regenerates it and fails on a difference.

Demos and documentation

  • Six application screens built from the public headers alone: a revenue
    dashboard, a weather desk, a plant supervisory HMI, a production line monitor,
    a grid control desk and a logistics control tower. They link gbui::gbui and
    nothing else, so a change that makes them awkward has made the library
    awkward.
  • The demos run in the browser through WebAssembly, rasterised on the CPU
    into a <canvas> — the same source, with no DOM inside the rectangle.
  • A documentation site on VitePress, published per version: the current
    release at the root and every archived one at its own address, built from its
    own tag.
  • The source is shown first and the screen runs when the reader asks, so nothing
    downloads until they press Run.

Build and packaging

  • Installable with CMake, as a static or shared library, and consumed from
    outside the tree in CI to prove the install actually works.
  • SDL2 is optional: without it everything still builds and every test still
    passes, and only Window::create changes.
  • CI builds, tests, sanitises and lints on Linux, macOS and Windows, with
    warnings as errors.

Fixed

  • Every std::optional in an options struct has a default member initialiser,
    so a designated-initialiser call site cannot leave one indeterminate.
  • Four things that only writing six screens against the library could find — see
    acf2897.
  • The runner works on a fresh machine and on MSVC; the gallery example the
    gitignore was hiding is committed.

Known at the time

  • No accessibility tree, and no bridge for a screen reader.
  • No text shaping, so Arabic, Devanagari and emoji are wrong.
  • No GPU painter — the Painter interface is six methods precisely so one can
    be written.
  • No image decoding.