Giter Club home page Giter Club logo

dvui's Introduction

DVUI - Immediate Zig GUI for Apps and Games

A Zig GUI toolkit for whole applications or extra debugging windows in an existing application.

Tested with Zig 0.13

How to run the built-in examples:

  • zig build sdl-standalone
  • zig build sdl-ontop
  • zig build raylib-standalone
  • zig build raylib-ontop
  • zig build web-test

This document is a broad overview. See implementation details for how to write and modify widgets.

Below is a screenshot of the demo window, whose source code can be found at src/Examples.zig.

Screenshot of DVUI Standalone Example (Application Window)

Projects using DVUI

Features

  • Immediate Mode Interface
  • Process every input event (suitable for low-fps situations)
  • Use for whole UI or for debugging on top of existing application
  • Existing backends
  • Icon support via TinyVG
  • Raster image support via stb_image
  • Font support
  • Touch support
    • Including selection draggables in text entries
  • Animations
  • Themes
  • FPS throttling

Usage

DVUI Demo is a template project you can use as a starting point.

The build.zig and build.zig.zon files there show how to reference dvui as a zig dependency.

Built-in Widgets

  • Text Entry (single and multiline)
    • Includes touch support (selection draggables and menu)
  • Number Entry
    • Supports all Integer and Floating Point types
  • Text Layout
    • Parts can be clickable
    • Parts separately styled
  • Floating Window
  • Menu
  • Popup/Context Window
  • Scroll Area
  • Button
  • Multi-line label
    • Can be clickable for links
  • Slider
  • SliderEntry
    • Combo slider and text entry
  • Checkbox
  • Radio Buttons
  • Toast
  • Panes with draggable sash
  • Dropdown
  • Reorderable Lists
    • Drag to reorder/remove/add
  • Missing Widgets for now
    • Data Grid
    • Flex Layout
    • Docking

Design

Immediate Mode

if (try dvui.button(@src(), "Ok", .{}, .{})) {
  dialog.close();
}

Widgets are not stored between frames like in traditional gui toolkits (gtk, win32, cocoa). dvui.button() processes input events, draws the button on the screen, and returns true if a button click happened this frame.

For an intro to immediate mode guis, see: https://github.com/ocornut/imgui/wiki#about-the-imgui-paradigm

Advantages

  • Reduce widget state
    • example: checkbox directly uses your app's bool
  • Reduce gui state
    • the widgets shown each frame directly reflect the code run each frame
    • harder to be in a state where the gui is showing one thing but the app thinks it's showing something else
    • don't have to clean up widgets that aren't needed anymore
  • Functions are the composable building blocks of the gui
    • since running a widget is a function, you can wrap a widget easily
// Let's wrap the sliderEntry widget so we have 3 that represent a Color
pub fn colorSliders(src: std.builtin.SourceLocation, color: *dvui.Color, opts: Options) !void {
    var hbox = try dvui.box(src, .horizontal, opts);
    defer hbox.deinit();

    var red: f32 = @floatFromInt(color.r);
    var green: f32 = @floatFromInt(color.g);
    var blue: f32 = @floatFromInt(color.b);

    _ = try dvui.sliderEntry(@src(), "R: {d:0.0}", .{ .value = &red, .min = 0, .max = 255, .interval = 1 }, .{ .gravity_y = 0.5 });
    _ = try dvui.sliderEntry(@src(), "G: {d:0.0}", .{ .value = &green, .min = 0, .max = 255, .interval = 1 }, .{ .gravity_y = 0.5 });
    _ = try dvui.sliderEntry(@src(), "B: {d:0.0}", .{ .value = &blue, .min = 0, .max = 255, .interval = 1 }, .{ .gravity_y = 0.5 });

    color.r = @intFromFloat(red);
    color.g = @intFromFloat(green);
    color.b = @intFromFloat(blue);
}

Drawbacks

  • Hard to do fire-and-forget
    • example: show a dialog with an error message from code that won't be run next frame
    • dvui includes a retained mode space for dialogs and toasts for this
  • Hard to do dialog sequence
    • retained mode guis can run a modal dialog recursively so that dialog code can only exist in a single function
    • dvui retained dialogs can be chained together for this

Handle All Events

DVUI processes every input event, making it useable in low framerate situations. A button can receive a mouse-down event and a mouse-up event in the same frame and correctly report a click. A custom button could even report multiple clicks per frame. (the higher level dvui.button() function only reports 1 click per frame)

In the same frame these can all happen:

  • text entry field A receives text events
  • text entry field A receives a tab that moves keyboard focus to field B
  • text entry field B receives more text events

Because everything is in a single pass, this works in the normal case where widget A is run before widget B. It doesn't work in the opposite order (widget B receives a tab that moves focus to A) because A ran before it got focus.

Floating Windows

This library can be used in 2 ways:

  • as the gui for the whole application, drawing over the entire OS window
  • as floating windows on top of an existing application with minimal changes:
    • use widgets only inside dvui.floatingWindow() calls
    • dvui.addEvent... functions return false if event won't be handled by dvui (main application should handle it)
    • change dvui.cursorRequested() to dvui.cursorRequestedFloating() which returns null if the mouse cursor should be set by the main application

Floating windows and popups are handled by deferring their rendering so that they render properly on top of windows below them. Rendering of all floating windows and popups happens during window.end().

FPS throttling

If your app is running at a fixed framerate, use window.begin() and window.end() which handle bookkeeping and rendering.

If you want to only render frames when needed, add window.beginWait() at the start and window.waitTime() at the end. These cooperate to sleep the right amount and render frames when:

  • an event comes in
  • an animation is ongoing
  • a timer has expired
  • user code calls dvui.refresh(null, ...) (if your code knows you need a frame after the current one)
  • a background thread calls dvui.refresh(window, ...) which in turn calls backend.refresh()

window.waitTime() also accepts a max fps parameter which will ensure the framerate stays below the given value.

window.beginWait() and window.waitTime() maintain an internal estimate of how much time is spent outside of the rendering code. This is used in the calculation for how long to sleep for the next frame.

The estimate is visible in the demo window Animations > Clock > "Estimate of frame overhead". The estimate is only updated on frames caused by a timer expiring (like the clock example), and it starts at 1ms.

Widget init and deinit

The easiest way to use widgets is through the high-level functions that create and install them:

{
    var box = try dvui.box(@src(), .vertical, .{.expand = .both});
    defer box.deinit();

    // widgets run here will be children of box
}

These functions allocate memory for the widget onto an internal arena allocator that is flushed each frame.

Instead you can allocate the widget on the stack using the lower-level functions:

{
    var box = BoxWidget.init(@src(), .vertical, false, .{.expand = .both});
    // box now has an id, can look up animations/timers

    try box.install();
    // box is now parent widget

    try box.drawBackground();
    // might draw the background in a different way

    defer box.deinit();

    // widgets run here will be children of box
}

The lower-level functions give a lot more customization options including animations, intercepting events, and drawing differently.

Start with the high-level functions, and when needed, copy the body of the high-level function and customize from there.

Parent, Child, and Layout

The primary layout mechanism is nesting widgets. DVUI keeps track of the current parent widget. When a widget runs, it is a child of the current parent. A widget may then make itself the current parent, and reset back to the previous parent when it runs deinit().

The parent widget decides what rectangle of the screen to assign to each child.

Usually you want each part of a gui to either be packed tightly (take up only min size), or expand to take the available space. The choice might be different for vertical vs. horizontal.

When a child widget is laid out (sized and positioned), it sends 2 pieces of info to the parent:

  • min size
  • hints for when space is larger than min size (expand, gravity_x, gravity_y)

If parent is not expanded, the intent is to pack as tightly as possible, so it will give all children only their min size.

If parent has more space than the children need, it will lay them out using the hints:

  • expand - whether this child should take more space or not
  • gravity - if not expanded, where to position child in larger space

Appearance

Each widget has the following options that can be changed through the Options struct when creating the widget:

  • margin (space outside border)
  • border (on each side)
  • padding (space inside border)
  • min_size_content (margin/border/padding added to get min size)
  • background (fills space inside border with background color)
  • corner_radius (for each corner)
  • colors (either RGBA value or named)
    • example RGBA .color_text = .{ .color = .{ .r = 0xe0, .g = 0x1b, .b = 0x24 } }
    • example named .color_text = .{ .name = .err } (get current theme's color_err)
    • color_accent
    • color_text
    • color_text_press
    • color_fill
    • color_fill_hover
    • color_fill_press
    • color_border
  • font_style (use theme's fonts)
    • or directly set font:
      • font

Each widget has its own default options. These can be changed directly:

dvui.ButtonWidget.defaults.background = false;

Themes can be changed between frames or even within a frame. The theme controls the fonts and colors referenced by font_style and named colors.

if (theme_dark) {
    win.theme = &dvui.Theme.AdwaitaDark;
}
else {
    win.theme = &dvui.Theme.AdwaitaLight;
}

The theme's color_accent is also used to show keyboard focus.

See implementation details for more information.

dvui's People

Contributors

alichraghi avatar anerruption avatar david-vanderson avatar desttinghim avatar iacore avatar knockerpulsar avatar r4gus avatar slimsag avatar visendev avatar wilsonk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

dvui's Issues

Color Picker Widget

Add support for a specialized color picker widget similar to the following.

Screenshot 2024-07-29 at 11 58 15 AM

.
The current implementation of a basic color picker in src/Example.zig looks like this

// Let's wrap the sliderEntry widget so we have 3 that represent a Color
pub fn rgbSliders(src: std.builtin.SourceLocation, color: *dvui.Color, opts: Options) !void {
    var hbox = try dvui.box(src, .horizontal, opts);
    defer hbox.deinit();

    var red: f32 = @floatFromInt(color.r);
    var green: f32 = @floatFromInt(color.g);
    var blue: f32 = @floatFromInt(color.b);

    _ = try dvui.sliderEntry(@src(), "R: {d:0.0}", .{ .value = &red, .min = 0, .max = 255, .interval = 1 }, .{ .gravity_y = 0.5 });
    _ = try dvui.sliderEntry(@src(), "G: {d:0.0}", .{ .value = &green, .min = 0, .max = 255, .interval = 1 }, .{ .gravity_y = 0.5 });
    _ = try dvui.sliderEntry(@src(), "B: {d:0.0}", .{ .value = &blue, .min = 0, .max = 255, .interval = 1 }, .{ .gravity_y = 0.5 });

    color.r = @intFromFloat(red);
    color.g = @intFromFloat(green);
    color.b = @intFromFloat(blue);
}

Window Freezes During Toast

Using the toast widget in the demo window causes the entire window to freeze for a few seconds.

If this is intended behavior, it should probably be adjusted because it feels bad when the application goes non-responsive as a user.

Platform: x86_64 Linux
Backend: SDL2

The same behavior also showed on my mac - the OS even gave me a spinning mouse pointer animation to show that the application was not responding.

0.13 support

C:\Users\theo\AppData\Local\zig\p\12206354e8e79f32158e3f279285efeebd4e15b6c8d1eeef4e2278420f0d0d08d96d\build.zig:145:37: error: no field named 'path' in union 'Build.LazyPath'
            .root_source_file = .{ .path = "web-test.zig" },
                                    ^~~~
C:\Users\theo\Documents\gentoo\sysroot\lib\std\Build.zig:2171:22: note: union declared here
pub const LazyPath = union(enum) {

Dropdown has no scroll if larger than window size

I just added a dropdown and filled it with a lot of items, but i can't see the bottom several items, even in full screen mode, therefore I can't choose those items. If I use the down key then the highlighter just disappears of the bottom of the screen also.

Perhaps there is an option I am unaware of...or I should be wrapping the dropdown?

A few bugs/problems

Hello!

First off, I wanted to say: thank you for making such GUI framework! It is amazing!!

With that said, I did encounter a few bugs and problems:

  • Trying to move any dialog window (so, a child window inside the main SDL window) causes a segmentation fault:
Segmentation fault at address 0x8
???:?:?: 0x7fec3bf66be0 in ??? (???)

Unfortunately, it doesn't give me any more information... is there a way to make it do so, or is it the maximum it can give? It seems pretty unhelpful...

  • When closing the window (and thus, quitting the program), it seems like there is some leaked memory from the GPA:
error(gpa): memory address 0x7f0ba42b4000 leaked: 
???:?:?: 0x34d08c in ??? (???)
???:?:?: 0x301b14 in ??? (???)
???:?:?: 0x2de9e5 in ??? (???)
???:?:?: 0x2e5bd6 in ??? (???)


error(gpa): memory address 0x7f0ba0ad6000 leaked: 
???:?:?: 0x35832b in ??? (???)
???:?:?: 0x3161e0 in ??? (???)
???:?:?: 0x2e27ed in ??? (???)
???:?:?: 0x2deb0a in ??? (???)


error(gpa): memory address 0x7f0ba42b3000 leaked: 
???:?:?: 0x34d8c9 in ??? (???)
???:?:?: 0x302140 in ??? (???)
???:?:?: 0x2deaf9 in ??? (???)
???:?:?: 0x2e5bd6 in ??? (???)


error(gpa): memory address 0x7f0bad5ab000 leaked: 
???:?:?: 0x3b081e in ??? (???)
???:?:?: 0x3a2362 in ??? (???)
???:?:?: 0x38dcd5 in ??? (???)
???:?:?: 0x36ebb5 in ??? (???)

I don't think it provides from my program, I double checked everything and I'm freeing all the memory I'm allocating. In fact, because of that, I don't even know where these memory leaks are coming from... are they coming from the framework itself?

  • The standalone SDL on the repository doesn't compile. There seems to have been a typo or something: at line 69, there is this:
var scroll = try gui.scrollArea(@src(), .{ .expand = .both, .color_style = .window });

However, this is incorrect, as it requires 3 arguments instead of 2. Here, the initialization options are missing. This is correct, however:

var scroll = try gui.scrollArea(@src(), .{}, .{ .expand = .both, .color_style = .window });
  • And finally, the standalone SDL demo in the gui-demo repository doesn't compile. Unfortunately, I haven't been able to figure this one out:
error: deprecated; use @memset instead
pub const set = @compileError("deprecated; use @memset instead");

However, I did notice that the repository uses an old version of gui (as can be seen by the commit hash inside the build.zig.zon file). I've tried updating the version (by changing the commit hash), but it still didn't work...

Anyway, sorry for this big wall of text, and again, thank you for this amazing library!

Scaling issue on KDE Plasma 5 (X11 and Wayland)

I think ever since commit b5d4f22, I've been getting weird scaling issues within DVUI on KDE Plasma 5 (Void Linux), X11 and Wayland:

image

As you can see, everything's just way too big. I tried on Cinnamon (LMDE) and the issue does not occur.

SDL2 Missing; Build Issue on Windows 10

Hi! Just wanted to confirm that, from my understanding, the library is supposed to build and link SDL2 by itself?

I'm currently on the latest update of Windows 10 using Zig version 0.12.0-dev.309+402468b21. When I try to build the dvui-demo repository, I run into an issue that states:
error: unable to find Dynamic system library 'SDL2' using strategy 'paths_first'. searched paths: none.

Is there any direction anyone can point me towards in resolving this issue?

Thank you!

Clicking on Submenu disables it

Not sure if this is an actual bug or expected behavior, but if you open the sdl-test binary and click on OpenDemoWindow, then drop down the Menus, then hover over the File menu, then left click on the 'submenu' option. The submenu disappears and won't popup again until you click on 'submenue' again or leave the File popup area and re-enter it again, or click on 'submenu' again.

Again, I am not sure if this is intended behavior or not. Just seemed a little off to me, so I thought I would open an issue, just in case it is a bug.

Change "@fabs" to "@abs".

dvui/src/dvui.zig
dvui/src/tinyvg/rendering.zig
dvui/sdl-test.zig

I'm checking. I think my version of zig is a week old. I'll download today's version. and recheck.

be a good dependency (easy to use)

I wish that the end user of dvui doesn't need to know about its internally headers and libraries.

Since the inclusion of stb_image I had to modify my project using dvui again.

I tried to make it simpler here but Zig is not being nice.

Better API for writing widget data

Currently dvui.dataSet copies too much data.

I have the following code:

    fn widgetDo(this: *@This(), float: *dvui.FloatingWindowWidget, key: []const u8) !void {
        var code_buffer = dvui.dataGet(null, float.wd.id, key, [1024]u8) orelse .{0} ** 1024;
        defer dvui.dataSet(null, float.wd.id, key, code_buffer);

Here, 1024 bytes of data is copied per frame per widget.

I hope I can just get a slice to the memory location, then I can modify that directly.

Scroll bars on sdl-test main window

When I start sdl-test on my Linux machine everything seems fine, but the 'floating window' button is not visible on the opening window. I have to grab the bottom of the window and enlarge it to see that there is a button at the bottom. I assume there should be a vertical scrollbar available when all the widgets don't fit into the current window?

Or at least a minimum calculated size that requires the main window be large enough to show all currently 'visible' widgets? This option could lead to bad behavior, however, if a lot of widgets are on an opening page.

I think that a vertical scroll bar does pop up if a textentry widget is too large to contain in the main window (at least this happens in the example I am programming). So perhaps there is pre-existing checks/code that just needs to be used for other widget types?

Discussion space

A few people have asked for some kind of discussion space. This issue is to gather feedback.

The options I know of are:

  • discord
  • matrix
  • topic on ziggit.dev
  • irc

I asked Loris Cro for advice. He confirmed that most people are using one of those options, and advised us that the technical merits of the choice matter much less than people's willingness to use it.

Personally I don't have strong opinions about which option to go with, but will make a decision soon so we don't get stuck. I also have no problems trying one for a while and then switching. I'm sure @iacore will help keep me honest here!

Please comment if you have any kind of opinion about this.

Raylib Backend?

Hello, dvui seems like a really cool project. It looks like this library is designed to support many different types of backends. I was wondering if a raylib backend would be a feasible option.

Many games use raylib so it would be nice to be able to integrate dvui into raylib projects for ui.

use proper colorspace for color operations

cannot lighten black

0 * (1 + y) == 0

dvui/src/Color.zig

Lines 28 to 35 in 811e8d9

pub fn lighten(x: Color, y: f32) Color {
return Color{
.r = @as(u8, @intFromFloat(@min(@as(f32, @floatFromInt(x.r)) * (1 + y), 255))),
.g = @as(u8, @intFromFloat(@min(@as(f32, @floatFromInt(x.g)) * (1 + y), 255))),
.b = @as(u8, @intFromFloat(@min(@as(f32, @floatFromInt(x.b)) * (1 + y), 255))),
.a = x.a,
};
}

Batching draw calls

During my work with draw calls (#83 (comment)), I created the following new API design

Currently:

  1. dvui allocates temporary memory
  2. dvui populates vtx and idx
  3. dvui calls backend.drawClippedTriangles with temporary memory

I propose the following new API:

  1. dvui calls backend.allocDrawCall(vtx_len, idx_len) to allocate memory
  2. dvui populates vtx and idx
  3. dvui calls backend.drawClippedTriangles with temporary memory

benefit: zero copy. backend.allocDrawCall decides the lifetime of memory returned.
if no batching, lifetime = until drawClippedTriangles is called
if batching, lifetime = until end of frame

Issue with TextEntry and ctrl-x

I am on the newest version as of today (May 20, 2024 with zig-0.12) with a very up-to-date Arch Linux variant. Things build and run fine until I open the run-ontop-sdl examples multiline TextEntry widget and try to ctrl-x some code. I just typed in random 'asdf' keys on about 6 lines and then highlighted about 3 chars and tried to cut them...this originally caused a crash (see fixes in #70), and after that was fixed (perhaps improperly, now that I think about it??), I am seeing the line AFTER the line where I am cutting being pulled up 'into' the line where the cut is? Weird.

Then if I cut again, the next line down is also pulled up into the current line but the current line after the 'cut' is all just the last two characters of the line BELOW the current line repeated!?!? Double weird ;)

Anyhow, this may be due to my fix being incomplete or just completely wrong. I figured I would open this, just to keep track of the behavior anyways.

build error

steps [3/15] zig build-lib freetype Debug native-native... Compile C Objects [2/43] svg.c.steps [3/15] zig build-lib freetype Debug native-native... Compile C Objects [3/43] smo...steps [3/15] zig build-lib freetype Debug native-native... Compile C Objects [6/43] ras...steps [3/15] zig build-lib freetype Debug native-native... Compile C Objects [6/43] ras...steps [3/15] zig build-lib freetype Debug native-native... Compile C Objects [8/43] psh...steps [3/15] zig build-lib freetype Debug native-native... Compile C Objects [9/43] psa...steps [3/15] zig build-lib freetype Debug native-native... Compile C Objects [10/43] pf...steps [3/15] zig build-lib freetype Debug native-native... Compile C Objects [11/43] pc...steps [3/15] zig build-lib freetype Debug native-native... Compile C Objects [13/43] ft...steps [3/15] zig build-lib freetype Debug native-native... Compile C Objects [15/43] cf...steps [3/15] zig build-lib freetype Debug native-native... Compile C Objects [19/43] ft...steps [3/15] zig build-lib freetype Debug native-native... Compile C Objects [22/43] ft...steps [3/15] zig build-lib freetype Debug native-native... Compile C Objects [28/43] ft...steps [3/15] zig build-lib freetype Debug native-native... Compile C Objects [38/43] ft...steps [4/15] zig build-lib dvui_libs Debug native... Compile C Objects [1/2] stb_image_...steps [4/15] zig build-lib dvui_libs Debug native... Compile C Objects [1/2] stb_image_...steps [4/15] zig build-lib dvui_libs Debug native... Compile C Objects [1/2] stb_image_...steps [4/15] zig build-lib dvui_libs Debug native... Compile C Objects [1/2] stb_image_...zig build-exe ontop-sdl Debug native: error: the following command failed with 1 compilation errors:
/usr/local/zig11/zig11 build-exe /home/nil/zig/dvui/dvui/ontop-sdl.zig /home/nil/zig/dvui/dvui/zig-cache/o/3e4d7d69583d2419a8b24d8ee78290d4/libdvui_libs.a /home/nil/zig/dvui/dvui/zig-cache/o/2b93cede3f02ffa0f094ae72a2c402be/libfreetype.a -D_REENTRANT -I/usr/include/SDL2 -lSDL2 -lc --cache-dir /home/nil/zig/dvui/dvui/zig-cache --global-cache-dir /home/nil/.cache/zig --name ontop-sdl --mod dvui::/home/nil/zig/dvui/dvui/src/dvui.zig --mod SDLBackend:dvui:/home/nil/zig/dvui/dvui/src/backends/SDLBackend.zig --deps dvui,SDLBackend -I /home/nil/.cache/zig/p/1220b305af272ac3026704b8d7e13740350daf59a6a95e45dedb428c55933e3df925/include -I /home/nil/zig/dvui/dvui/src/stb --listen=- 
zig build-exe standalone-sdl Debug native: error: the following command failed with 1 compilation errors:
/usr/local/zig11/zig11 build-exe /home/nil/zig/dvui/dvui/standalone-sdl.zig /home/nil/zig/dvui/dvui/zig-cache/o/3e4d7d69583d2419a8b24d8ee78290d4/libdvui_libs.a /home/nil/zig/dvui/dvui/zig-cache/o/2b93cede3f02ffa0f094ae72a2c402be/libfreetype.a -D_REENTRANT -I/usr/include/SDL2 -lSDL2 -lc --cache-dir /home/nil/zig/dvui/dvui/zig-cache --global-cache-dir /home/nil/.cache/zig --name standalone-sdl --mod dvui::/home/nil/zig/dvui/dvui/src/dvui.zig --mod SDLBackend:dvui:/home/nil/zig/dvui/dvui/src/backends/SDLBackend.zig --deps dvui,SDLBackend -I /home/nil/.cache/zig/p/1220b305af272ac3026704b8d7e13740350daf59a6a95e45dedb428c55933e3df925/include -I /home/nil/zig/dvui/dvui/src/stb --listen=- 
zig build-exe sdl-test Debug native: error: the following command failed with 1 compilation errors:
/usr/local/zig11/zig11 build-exe /home/nil/zig/dvui/dvui/sdl-test.zig /home/nil/zig/dvui/dvui/zig-cache/o/3e4d7d69583d2419a8b24d8ee78290d4/libdvui_libs.a /home/nil/zig/dvui/dvui/zig-cache/o/2b93cede3f02ffa0f094ae72a2c402be/libfreetype.a -D_REENTRANT -I/usr/include/SDL2 -lSDL2 -lc --cache-dir /home/nil/zig/dvui/dvui/zig-cache --global-cache-dir /home/nil/.cache/zig --name sdl-test --mod dvui::/home/nil/zig/dvui/dvui/src/dvui.zig --mod SDLBackend:dvui:/home/nil/zig/dvui/dvui/src/backends/SDLBackend.zig --deps dvui,SDLBackend -I /home/nil/.cache/zig/p/1220b305af272ac3026704b8d7e13740350daf59a6a95e45dedb428c55933e3df925/include -I /home/nil/zig/dvui/dvui/src/stb --listen=- 
Build Summary: 5/15 steps succeeded; 3 failed (disable with --summary none)
install transitive failure
โ”œโ”€ standalone-sdl transitive failure
โ”‚  โ””โ”€ install standalone-sdl transitive failure
โ”‚     โ””โ”€ zig build-exe standalone-sdl Debug native 1 errors
โ”œโ”€ ontop-sdl transitive failure
โ”‚  โ””โ”€ install ontop-sdl transitive failure
โ”‚     โ””โ”€ zig build-exe ontop-sdl Debug native 1 errors
โ””โ”€ compile-sdl-test transitive failure
   โ””โ”€ install sdl-test transitive failure
      โ””โ”€ zig build-exe sdl-test Debug native 1 errors
/usr/local/zig11/lib/std/fmt.zig:182:13: error: too few arguments
            @compileError("too few arguments");
            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/local/zig11/lib/std/fmt.zig:182:13: error: too few arguments
            @compileError("too few arguments");
            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/local/zig11/lib/std/fmt.zig:182:13: error: too few arguments
            @compileError("too few arguments");
            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

on-top demo draws over application

Hello, thanks so far for your work on this library, quite cool to see something like this written for zig! Also, sorry if this issue is more fit for the demo's repo, I'm not quite sure if it's an issue with the demo itself or the library.

Anyway, I seem to have some trouble rendering dvui on top of my existing application. Here's some context: I draw pixels to some buffer, then I draw this buffer onto the screen. At first I used minifb, but it seems to not work well with dvui, so I transitioned to SDL.

I'm pretty much using the on-top demo, but adding a bit of application specific initialization and the following block inside the rendering part of the main loop:

  // Somehwere before the main loop:
  var texture = c.SDL_CreateTexture(window.renderer, c.SDL_PIXELFORMAT_ARGB8888, c.SDL_TEXTUREACCESS_STREAMING, width, height);

  // Somewhere inside the main loop
        window.backend.clear();
        try dvui_stuff();

        // Request from SDL to map the texture to some memory location so we can write to it
        var pixels: ?*anyopaque = null;
        var pitch: c_int = undefined;
        _ = c.SDL_LockTexture(texture, null, &pixels, &pitch);

        var pixelsAsU32: [*]u32 = @alignCast(@ptrCast(pixels));

        // For each pixel, copy over some data (in this case, just display bright purple)
        for (pixels) |pixel| {
                const x = pixel.x;
                const y = pixel.y;
                const index = x + (height - y - 1) * width;  // Defined somewhere else in the program
                pixelsAsU32[index] = RGBA(1, 0, 1, 1); // Convert RGBA to a u32        
        }

        c.SDL_UnlockTexture(texture);

        // Render the whole texture onto the whole screen.
        _ = c.SDL_RenderCopy(window.renderer, texture, null, null);

        // marks end of dvui frame, don't call dvui functions after this
        // - sends all dvui stuff to backend for rendering, must be called before renderPresent()
        _ = try win.end(.{});

Running the program in renderdoc, it shows that the texture was properly uploaded to the GPU (although flipped), when I do anything with dvui, the call to draw my texture draws only a part of it. If I don't call dvui_stuff(), the texture is rendered fully.

When observing the calls in renderdoc, specifically the call to draw my texture, there seems to be a glEnable(GL_SCISSORS_TEST) that doesn't appear when dvui_stuff() is commented, but I'm not familiar with OpenGL's scissor feature much, so I'm not sure if that's the root cause of the issue. It's very suspicious though.

When running the application, it shows the texture I'm drawing and dvui for the first frame only, then only dvui from then on out with a black background. If I move the dvui floating window, I can see "ghosts" of my texture, but they seem to follow the floating windows and disappear behind them.

At this point, I'm not sure if it's something in my code or perhaps with dvui.

minor memory leak

To reproduce, run zig build run-standalone-sdl and close the window. I haven't looked into where the error comes from.

โฏ zig build run-standalone-sdl
info(dvui): window logical Size{ 668 524 } pixels Size{ 668 524 } natural scale 1 initial content scale 1 snap_to_pixels true

debug(dvui): FontCacheGet creating font size 13 name "Vera"
debug(dvui): - size 13 ascent 10 height 13
debug(dvui): FontCacheGet creating font size 15 name "VeraBd"
debug(dvui): - size 15 ascent 12 height 15
error(gpa): memory address 0x7d62d26ee800 leaked: 
/home/user/.zvm/0.13.0/lib/std/hash_map.zig:1605:53: 0x13adaca in allocate (standalone-sdl)
            const slice = try allocator.alignedAlloc(u8, max_align, total_size);
                                                    ^
/home/user/.zvm/0.13.0/lib/std/hash_map.zig:1562:29: 0x1392841 in grow (standalone-sdl)
            try map.allocate(allocator, new_cap);
                            ^
/home/user/.zvm/0.13.0/lib/std/hash_map.zig:1512:30: 0x135dda4 in growIfNeeded (standalone-sdl)
                try self.grow(allocator, capacityForSize(self.load() + new_count), ctx);
                             ^
/home/user/.zvm/0.13.0/lib/std/hash_map.zig:1333:34: 0x13318fd in getOrPutContextAdapted__anon_30681 (standalone-sdl)
                self.growIfNeeded(allocator, 1, ctx) catch |err| {
                                 ^
/home/user/.zvm/0.13.0/lib/std/hash_map.zig:1318:56: 0x12fe1cb in getOrPutContext (standalone-sdl)
            const gop = try self.getOrPutContextAdapted(allocator, key, ctx, ctx);
                                                       ^
/home/user/.zvm/0.13.0/lib/std/hash_map.zig:1244:52: 0x12950b1 in putContext (standalone-sdl)
            const result = try self.getOrPutContext(allocator, key, ctx);
                                                   ^

error(gpa): memory address 0x7d62c6566000 leaked: 
/home/user/.zvm/0.13.0/lib/std/hash_map.zig:1605:53: 0x13adaca in allocate (standalone-sdl)
            const slice = try allocator.alignedAlloc(u8, max_align, total_size);
                                                    ^
/home/user/.zvm/0.13.0/lib/std/hash_map.zig:1562:29: 0x1392841 in grow (standalone-sdl)
            try map.allocate(allocator, new_cap);
                            ^
/home/user/.zvm/0.13.0/lib/std/hash_map.zig:1512:30: 0x135dda4 in growIfNeeded (standalone-sdl)
                try self.grow(allocator, capacityForSize(self.load() + new_count), ctx);
                             ^
/home/user/.zvm/0.13.0/lib/std/hash_map.zig:1333:34: 0x13318fd in getOrPutContextAdapted__anon_30681 (standalone-sdl)
                self.growIfNeeded(allocator, 1, ctx) catch |err| {
                                 ^
/home/user/.zvm/0.13.0/lib/std/hash_map.zig:1318:56: 0x12fe1cb in getOrPutContext (standalone-sdl)
            const gop = try self.getOrPutContextAdapted(allocator, key, ctx, ctx);
                                                       ^
/home/user/.zvm/0.13.0/lib/std/hash_map.zig:1244:52: 0x12950b1 in putContext (standalone-sdl)
            const result = try self.getOrPutContext(allocator, key, ctx);
                                                   ^

reached unreachable code

I just encountered the following bug when using a Button with a Text, e.g.: https://github.com/r4gus/keypass/blob/4a480b8eda69ccd1be8549e1a823244996372283/src/gui/login.zig#L103

thread 5585 panic: reached unreachable code
/home/sugar/.cache/zig/p/1220bff2951dbed145107b567ad05ae4830bde54fb54d0d96f5297f7aeccfe7dc7c7/src/Options.zig:152:99: 0x322e16 in color (passkeez)
        .press_text => ret = cs.press_text orelse dvui.themeGet().style_content.press_text orelse unreachable,
                                                                                                  ^
/home/sugar/.cache/zig/p/1220bff2951dbed145107b567ad05ae4830bde54fb54d0d96f5297f7aeccfe7dc7c7/src/dvui.zig:7938:83: 0x35d5be in button (passkeez)
    if (captured(bw.wd.id)) options = options.override(.{ .color_text = opts.color(.press_text) });
                                                                                  ^
/home/sugar/SourceCode/keypass/src/gui/login.zig:103:28: 0x36da83 in login_frame (passkeez)
        if (try dvui.button(@src(), "Unlock", .{}, .{
                           ^
/home/sugar/SourceCode/keypass/src/gui.zig:79:34: 0x3964b2 in dvui_frame (passkeez)
        .login => try login_frame(),
                                 ^
/home/sugar/SourceCode/keypass/src/main.zig:88:27: 0x39d21b in main (passkeez)
        try gui.dvui_frame();
                          ^
/usr/local/bin/zig-linux-x86_64-0.11.0/lib/std/start.zig:574:37: 0x39f02e in main (passkeez)
            const result = root.main() catch |err| {
                                    ^
???:?:?: 0x7f3c120c8ccf in ??? (libc.so.6)
Unwind information for `libc.so.6:0x7f3c120c8ccf` was not available, trace may be incomplete

run passkeez: error: the following command terminated unexpectedly:
/home/sugar/SourceCode/keypass/zig-out/bin/passkeez 
Build Summary: 8/10 steps succeeded; 1 failed (disable with --summary none)
run transitive failure
โ””โ”€ run passkeez failure
error: the following build command failed with exit code 1:
/home/sugar/SourceCode/keypass/zig-cache/o/1cab198d13bc6a121cff9c891e060197/build /usr/local/bin/zig-linux-x86_64-0.11.0/zig /home/sugar/SourceCode/keypass /home/sugar/SourceCode/keypass/zig-cache /home/sugar/.cache/zig run

Usually I compile with -Doptimize=ReleaseSmall so this is the first time I encountered the error but it's still present in the most recent commit (1220e30b896597e843a23df3c15e9b126ed4a3d0fb042bfb7c85f7c10827bd6e66a7).

Feature request: float/vector draggable text field

Hello, thanks for the work on this library so far!

I think the inclusion of some float/vector widgets might be a big boon to the library in terms of ease of use. What I'm aiming for is something similar to ImGui's SliderFloat (correct me if I'm wrong), where you pass in some floats, and the data is updated as you drag or modify the text field. This would mix the functionality of sliders and text fields, with the added bonus of perhaps not needing a min/max like a real slider, just some increment speed.

So it would be something like:

const foo = @Vector(3, f32) { 420, 1337, 0 };
dvui.sliderFloat(@src(), f32, 3, &foo, ..); 

Where .. denotes any extra needed parameters (init options and so on).

Forgive me if I got some details wrong, can't quite fire up ImGui's demo right now. I also understand if you're not interested in emulating ImGui. If so, feel free to close this issue.

weird interaction in demo window

  1. swish swash i feel dizzy
  2. it doesn't remember the position (being on right panel) when the window is resized and back
Peek.2023-11-18.06-30.mp4

Document Backend Functions

Provide documentation on the precise purpose of each Backend function. This will make it easier for contributors to add implement new backends

typo in the usage section of README.md

the code segment for build.zig should use the variable name dvui_dep and not dep_dvui, plus this matches the usage of dvui demo.

I would also like to suggest to provide a release or maybe suggest commit hash to start from but I understand if it is not possible since it is cumbersome to manage (maybe add a note to use the same url and hash from in dvui-demo).

Thanks for the amazing library.

Menu Ghosts ๐Ÿ‘ป

( BTW: readme and readme-implementation have been very helpful. )

I was wondering if you had any thoughts or ideas about what could be causing this.

( Thanks again. )

I am vendoring dvui. The ghosts started happening after I cloned a newer version of dvui weeks ago. I cloned the latest version this morning. I get no error messages in your log.

Open the app.

  1. My app has a main menu at the top of the window. Below that is the content selected from the main menu.
  2. Below is my app upon opening it displays my Example screen.

The application at startup displays my example screen.

Open the app and then select Edit in the main menu.

  1. That will display the Edit screen in the content area below the main menu.
  2. The Edit screen is simple and displays properly.

The Edit screen displays properly.

Open the app and then select Contacts in the main menu.

  1. Lets go back to the app's opening screen.
  2. I click on Contacts in the main menu
  3. The Contact screen only renders it's menu icon.
    • It does not render more of it's content.
  4. The ghost of the main menu remains.
    • As I move the mouse the main menu's items (Example, Edit, Remove, Close Menu) appear and disappear.
    • Debug indicates that the main menu is not there.

The Contacts screen does not display properly.

Click on the Contact screen's menu icon

  1. A ghost of the open main menu remains.
  2. The Contact screen's menu appears.
  3. The Contact screen's content is rendered over the Example screen's content.

The Contacts screen with it's menu open.

Code

Below is my main menu code.

pub fn frame_main_menu(all_screens: *_framers_.Group) !void {
    var m = try dvui.menu(@src(), .horizontal, .{ .background = true, .expand = .horizontal });
    defer m.deinit();

    if (try dvui.menuItemIcon(@src(), "/home/nil/zig/test/doit", dvui.entypo.menu, .{ .submenu = true }, .{ .expand = .none })) |r| {
        var popup = try dvui.popup(@src(), dvui.Rect.fromPoint(dvui.Point{ .x = r.x, .y = r.y + r.h }), .{});
        defer popup.deinit();

        for (_main_menu_.sorted_main_menu_screen_names, 0..) |screen_name, id_extra| {
            if (try dvui.menuItemLabel(@src(), screen_name, .{}, .{ .id_extra = id_extra }) != null) {
                dvui.menuGet().?.close();
                // m.close();
                try all_screens.setCurrent(screen_name);
                return;
            }
        }
        var id_extra: usize = _main_menu_.sorted_main_menu_screen_names.len;
        if (try dvui.menuItemLabel(@src(), "Close Menu", .{}, .{ .id_extra = id_extra }) != null) {
            dvui.menuGet().?.close();
            // m.close();
            return;
        }
    }
}

Below is the code for my Contacts menu.

    fn frameFn(self_ptr: *anyopaque, arena: std.mem.Allocator) anyerror {
        var self: *Screen = @alignCast(@ptrCast(self_ptr));
        var layout = try dvui.box(@src(), .vertical, .{ .expand = .both });
        defer layout.deinit();

        {
            var m = try dvui.menu(@src(), .horizontal, .{});
            defer m.deinit();
            // var m = try dvui.menu(@src(), .horizontal, .{ .background = true, .expand = .horizontal });

            // Display the open book icon.
            // Return if:
            // 1. the user does not click on the menu icon.
            // 2. the drop down menu is not open.
            var r: ?dvui.Rect = try dvui.menuItemIcon(@src(), "open_book", dvui.entypo.open_book, .{ .submenu = true }, .{ .expand = .none });
            if (r == null) {
                return error.Null;
            }
            var fw = try dvui.popup(@src(), dvui.Rect.fromPoint(dvui.Point{ .x = r.?.x, .y = r.?.y + r.?.h }), .{});
            defer fw.deinit();

            // Add.
            if (try dvui.menuItemLabel(@src(), Add_label, .{}, .{ .expand = .horizontal, .id_extra = 0 }) != null) {
                dvui.menuGet().?.close();
                // m.close();
                self.selected_menu_item = menu_items.Add;
                return error.Null;
            }

            if (try dvui.menuItemLabel(@src(), "Debug", .{}, .{ .expand = .horizontal, .id_extra = 3 }) != null) {
                dvui.menuGet().?.close();
                // m.close();
                dvui.toggleDebugWindow();
                return error.Null;
            }

            if (try dvui.menuItemLabel(@src(), "Close Menu", .{}, .{ .expand = .horizontal, .id_extra = 4 }) != null) {
                dvui.menuGet().?.close();
                // m.close();
                return error.Null;
            }
        }

        {
            // The content area for a menu item.
            var vbox = try dvui.box(@src(), .vertical, .{ .expand = .both });
            defer vbox.deinit();

            // Display the selected menu item's content.
            switch (self.selected_menu_item) {
                .Add => {
                    // The Add menu_item's content is this same package's Add panel.
                    // Run the Add panel's fn frame.
                    try self.all_panels.Add.?.frame(arena);
                },
                .Edit => {
                    // The Edit menu_item's content is the Edit screen.
                    // Run the Edit screen's fn frameFn.
                    var behavior: *_framers_.Behavior = try self.all_screens.get("Edit");
                    var err = behavior.frameFn(behavior.implementor, arena);
                    if (err != error.Null) {
                        return err;
                    }
                },
                .Remove => {
                    // The Remove menu_item's content is the Remove screen.
                    // Run the Remove screen's fn frameFn.
                    var behavior: *_framers_.Behavior = try self.all_screens.get("Remove");
                    var err = behavior.frameFn(behavior.implementor, arena);
                    if (err != error.Null) {
                        return err;
                    }
                },
                .none => {},
            }
        }
        return error.Null;
    }
};

Zoom above 1 causes horizontal menu not to render.

At OKP in frontend/api.zig, lines 32 - 58, fn frame is shown below.

pub fn frame(arena: std.mem.Allocator) !void {
    // If set the zoom here then
    // 1. there is no app menu. ( a horzontal menu )
    // ๐Ÿ‘ the zoom works,

    // The main menu.
    try _main_menu_.frame(all_screens.?);

    // If set the zoom here then
    // ๐Ÿ‘ the app menu works and as expected has no zoom,
    // ๐Ÿ‘ the vertical tabs work correctly,
    // ๐Ÿ‘ the zoom works,
    // 1. my horizontal tabs are not visible, ( a copy of the horizontal menu code )
    // ๐Ÿ‘ but the default tab's content is visible as expected.

    // set the zoom.
    const theme: *dvui.Theme = dvui.themeGet();
    const font_body_size: f32 = theme.font_body.size;
    const scale_val: f32 = @round(font_body_size * 2.0) / font_body_size;
    var scaler = try dvui.scale(@src(), scale_val, .{ .expand = .both });
    defer scaler.deinit();

    // Only frame visible screens and panels.
    try all_screens.?.frame(arena);

    // If set the zoom here then obviously no zoom.
}

id_extra being integer is unsound

.id_extra should be at least a list of integers like .{0, 1, 2}
for convenience's sake, the slice should be comptime

it can also be (hash of the parent src + constant), but that's lower than hashing integers

Explanation

The title of this issue might as well be "Cardinality: Trees Hate Me".

You can't put integer sets into integer sets into integer sets into .... without global state

graphic

There are two ways of solving this problem.

  • global incremental counter (ugly)
  • hashing (need randomness and large enough space (number of bits used))

warn for unclosed widgets

Let's say I have the following code.


            try dvui.label(@src(), "domain", .{}, .{});
            var txt_domain = try dvui.textEntry(@src(), .{ .text = &this.b_domain }, .{});
            txt_domain.deinit();

this is what it looks like

image

However, I forgot to add txt_domain.deinit(); at first, and it looked like this

image

the other widgets are nowhere to be found, and no error in terminal either

I think there should be a message (like the duplicate id one) to warn the user something is wrong.

WebGL demo invalid utf8

I tried the demo with webgl2 and it looks/works well except the multiline TextEntry widget throws error.invalidUtf8 when I try to ctrl-x some text and repaste with ctrl-v. It seemed to work with two characters the first time I pasted, but failed the second time.

Tested again and it happened on the first go this time. Perhaps the demo hasn't been updated with the copyForwards fixes from yesterday? Or maybe something else :)

Obfuscation option for `TextEntryWidget`

It would be nice if users had the option to obfuscate the text displayed by the TextEntryWidget, e.g. the text hello is displayed as *****.

Users would have to specify if the text should be obfuscated and what character to use for the obfuscation.

.obfuscate = .{
    .enable = true,
    .char = '*',
}

Keeping the cursor position in a valid state would be a bit of a challenge when translating between one and multi byte chars. This could crash the application when not done correctly.

Asking for a password is a common scenario.

If this gets accepted I could implement a first draft.

Initializing the SDL Backend causes OOM error.

I am really excited about how dvui has developed. The dvui team is taking zig to another level.

I'm not vendoring dvui any more. I'm using build.zig.zon setup with zig fetch.

The dvui commit 27b59c5 was working great for me once I upgraded my app's zig code to 0.12.0.

So now I'm using the current commit which is 8645f75. My app's code is upgraded to zig 0.13.0 and builds without errors using zig 0.13.0.

When I run my app I get this out of memory error. Any ideas?

error: OutOfMemory
/usr/local/zig/lib/std/mem/Allocator.zig:225:89: 0x176305d in allocBytesWithAlignment__anon_30550 (crud)
    const byte_ptr = self.rawAlloc(byte_count, log2a(alignment), return_address) orelse return Error.OutOfMemory;
                                                                                        ^
/usr/local/zig/lib/std/mem/Allocator.zig:211:5: 0x16f9f56 in allocWithSizeAndAlignment__anon_27393 (crud)
    return self.allocBytesWithAlignment(alignment, byte_count, return_address);
    ^
/usr/local/zig/lib/std/mem/Allocator.zig:129:5: 0x1611788 in alloc__anon_24357 (crud)
    return self.allocAdvancedWithRetAddr(T, null, n, @returnAddress());
    ^
/usr/local/zig/lib/std/mem/Allocator.zig:326:21: 0x1759c74 in dupeZ__anon_30122 (crud)
    const new_buf = try allocator.alloc(T, m.len + 1);
                    ^
/usr/local/zig/lib/std/process.zig:1939:23: 0x175b2cd in createEnvironFromExisting (crud)
        envp_buf[i] = try arena.dupeZ(u8, mem.span(line));
                      ^
/usr/local/zig/lib/std/process/Child.zig:622:23: 0x16f1bb8 in spawnPosix (crud)
            break :m (try process.createEnvironFromExisting(arena, std.c.environ, .{
                      ^
/usr/local/zig/lib/std/process/Child.zig:242:9: 0x160cb78 in spawn (crud)
        return self.spawnPosix();
        ^
/home/nil/.cache/zig/p/1220f35274f3c12ac2dfc02edaa9c815cdcdfc657e564d74541aa9c3502a251d67c0/src/backends/SDLBackend.zig:138:21: 0x160b28a in init (crud)
                    try child.spawn();
                    ^
/home/nil/zig/crud/src/main.zig:42:23: 0x16e7278 in main (crud)
    var sdl_backend = try SDLBackend.init(.{
                      ^

Native file open dialogs

#23

Idea is to have dvui create a modal dialog and at the same time use a library to launch the platform's native file open dialog. When the native dialog is closed, the dvui dialog would extract the information and make it available to the app.

Proposed New Theme Fields

widget_borders: enum{only_necessary, always} = .only_necessary,

Option to enable borders on widgets that don't usually have borders (Buttons being the prime motivator)

default_corner_rounding: ?f32 = null,

Option to override the default rounding of corners. Themes with squared-off corners are now possible.

`Window.begin`: (panic: integer cast truncated bits)

Hy, I let my application with the following main loop run for a few hours unattended:

main_loop: while (true) {                                                                                
        defer _ = arena_allocator.reset(.free_all);
        // beginWait coordinates with waitTime below to run frames only when needed
        var nstime = win.beginWait(backend.hasEvent());

        // marks the beginning of a frame for dvui, can call dvui functions after this
        try win.begin(arena, nstime);

        // send all SDL events to dvui for processing
        const q = try backend.addAllEvents(&win);
        if (q) break :main_loop;

        try switch (state.state.getLast()) {
            .Login => dvui_login(),
            .Main => dvui_main(),
            .Up => dvui_up(),
        };

        try dvui_frame();

        // marks end of dvui frame, don't call dvui functions after this
        // - sends all dvui stuff to backend for rendering, must be called before renderPresent()
        const end_micros = try win.end(.{});

        // cursor management
        backend.setCursor(win.cursorRequested());

        // render frame to OS
        backend.renderPresent();

        // waitTime and beginWait combine to achieve variable framerates
        const wait_event_micros = win.waitTime(end_micros, null);
        backend.waitEventTimeout(wait_event_micros);
    }   

when I came back and moved the cursor the application panicked with the following message:

thread 38481 panic: integer cast truncated bits
/home/sugar/.cache/zig/p/122000a0a325ddba77dff830ab2df55a65a59df2e98fbaf609e26d34c028926bd1aa/src/dvui.zig:2706:42: 0x2bed1d in begin (scallop)
            micros_since_last = @as(u32, @intCast(@divFloor(nanos_since_last, std.time.ns_per_us)));
                                         ^
/home/sugar/scallop/platform-auth/main.zig:316:22: 0x30b250 in main (scallop)
        try win.begin(arena, nstime);
                     ^
/usr/local/bin/zig/lib/std/start.zig:574:37: 0x30db8e in main (scallop)
            const result = root.main() catch |err| {
                                    ^
../sysdeps/nptl/libc_start_call_main.h:58:16: 0x7fc42a629d8f in __libc_start_call_main (../sysdeps/x86/libc-start.c)
../csu/libc-start.c:392:3: 0x7fc42a629e3f in __libc_start_main_impl (../sysdeps/x86/libc-start.c)
???:?:?: 0x2ba404 in ??? (???)
???:?:?: 0x0 in ??? (???)
Aborted (core dumped)

System: Linux (Ubuntu 22.04), amd64

C API

I would love to use this in projects outside Zig as well.
Any idea if this project can provide a C API?

href/ url object

I think it would be nice to have a href/ url object that is a combination of label and button (but without the styling of a button). The expected behavior would be that dvui opens the url using the default browser on click.

Something like:

dvui.href("Text to display", "https://github.com/david-vanderson/dvui");

Cannot have multiple windows with same @src()

Example code:

for (0..10) |_| {
        var float = try dvui.floatingWindow(@src(), .{}, .{ .min_size_content = .{ .w = 150, .h = 100 }, .expand = .both });
        defer float.deinit();
}

console output:

dvui: subwindowAdd a2db04e4 is clearing some drawing commands (did you try to draw between subwindowCurrentSet and subwindowAdd?)
dvui: id 2c62666d was already used this frame (highlighting)
dvui: id 26697fd0 was already used this frame (highlighting)
dvui: id 99f2dbd7 was already used this frame (highlighting)
dvui: id d87dd749 was already used this frame (highlighting)
dvui: id aa418b01 was already used this frame (highlighting)
dvui: id 5914c0a9 was already used this frame (highlighting)
dvui: id a813efd3 was already used this frame (highlighting)
dvui: id 4c7298d7 was already used this frame (highlighting)
dvui: id 8b0523f2 was already used this frame (highlighting)
dvui: id f99307b8 was already used this frame (highlighting)
dvui: id e091231 was already used this frame (highlighting)
dvui: id a2db04e4 was already used this frame (highlighting)
...(repeat indefinitely)

Workaround

        var src = @src();
        src.line += @intCast(_id);
        src.line += 10000;
        var float = try dvui.floatingWindow(src, .{}, .{ .min_size_content = .{ .w = 150, .h = 100 }, .expand = .both });

Window positioning

Two problems:

  1. windows may be spawned outside of the viewport

example: (numbers are spawn order)

image

Suggestion: I think dvui should clip all windows inside the OS window, including on OS window resize.

  1. initial windows overlap each other completely. newly spawned windows do that too.

Suggestion: stagger the windows like

image

Horizontal scroll, using mouse wheel, pointer over content does not work.

I really like dvui!

With horizontal scrolls, the wheel works when the carrot is positioned over the grab-bars, but not when the carrot is positioned over the content to be scrolled.

An example is at https://github.com/JosephABudd/okp.

The file at https://github.com/JosephABudd/okp/blob/main/src/okp/frontend/screen/tab/htabs/screen.zig constructs the horizontal scroll area at line 52.

If you run okp and go to htabs from the main menu, you will see that I left the horizontal grab-bar under the tab-bar buttons. The tab buttons scroll horizontally when the carrot is positioned over the horizontal grab-bar track and the mouse wheel is turned. The tab buttons do not scroll horizontally when the carrot is positioned over the buttons and the mouse wheel is turned.

I just cloned and ran the okp so I know that I pushed everything up there. BTW my tab-bar is added to the end of src/dvui/dvui.zig

Backend functionality for easier integration into existing applications

The idea is to add functionality to the backends to separate dvui specific initialization - and general window / rendering initialization.

Existing applications will already have functionality for opening up a window, rendering a frame, etc... with a given backend.

The dvui user interface code will work the same whether the window was created by dvui or by a user application. So it would be nice to accommodate the use case of desiring this.

This proposal would allow an end user to use dvui only for adding widgets - the end user still manually creates a window and handles rendering frames and so on...

Possible implementation strategy -

  • Separate backend functions that manage the window into two separate functions. For example, init becomes initWindow and initDvuiState. initWindow creates the window, and initDvuiState initializes backend data fields.

  • Other functions like begin and end would also need to be split up in a similar way

  • Thus, users that also want dvui to create a window can call both functions, users that only want to use ui widgets can only initialize the dvui state

  • This differentiation could also be accomplished by giving all of the aformentioned functions another parameter to specify which behavior is desired.

Proposal: Remove Text Events

At the moment we have both text events and key change events. Why these are treated differently is not entirely clear - as they both handle key press events.

My understanding of why this is the case is because SDL treats these differently, but I find it confusing as to why these are not the same thing in dvui internally.

In particular, backends like raylib have to send 2 events for every key press. One Key event and one Text event.

This proposal is to remove TextEvents and make all keyboard status updates be sent thru Key Events to prevent the ambiguity between these two.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.